roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         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      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <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,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * 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
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @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)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <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
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @constructor
6172  * Create a new JsonReader
6173  * @param {Object} meta Metadata configuration options
6174  * @param {Object} recordType Either an Array of field definition objects,
6175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6176  */
6177 Roo.data.JsonReader = function(meta, recordType){
6178     
6179     meta = meta || {};
6180     // set some defaults:
6181     Roo.applyIf(meta, {
6182         totalProperty: 'total',
6183         successProperty : 'success',
6184         root : 'data',
6185         id : 'id'
6186     });
6187     
6188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6189 };
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6191     
6192     /**
6193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6194      * Used by Store query builder to append _requestMeta to params.
6195      * 
6196      */
6197     metaFromRemote : false,
6198     /**
6199      * This method is only used by a DataProxy which has retrieved data from a remote server.
6200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     read : function(response){
6205         var json = response.responseText;
6206        
6207         var o = /* eval:var:o */ eval("("+json+")");
6208         if(!o) {
6209             throw {message: "JsonReader.read: Json object not found"};
6210         }
6211         
6212         if(o.metaData){
6213             
6214             delete this.ef;
6215             this.metaFromRemote = true;
6216             this.meta = o.metaData;
6217             this.recordType = Roo.data.Record.create(o.metaData.fields);
6218             this.onMetaChange(this.meta, this.recordType, o);
6219         }
6220         return this.readRecords(o);
6221     },
6222
6223     // private function a store will implement
6224     onMetaChange : function(meta, recordType, o){
6225
6226     },
6227
6228     /**
6229          * @ignore
6230          */
6231     simpleAccess: function(obj, subsc) {
6232         return obj[subsc];
6233     },
6234
6235         /**
6236          * @ignore
6237          */
6238     getJsonAccessor: function(){
6239         var re = /[\[\.]/;
6240         return function(expr) {
6241             try {
6242                 return(re.test(expr))
6243                     ? new Function("obj", "return obj." + expr)
6244                     : function(obj){
6245                         return obj[expr];
6246                     };
6247             } catch(e){}
6248             return Roo.emptyFn;
6249         };
6250     }(),
6251
6252     /**
6253      * Create a data block containing Roo.data.Records from an XML document.
6254      * @param {Object} o An object which contains an Array of row objects in the property specified
6255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256      * which contains the total size of the dataset.
6257      * @return {Object} data A data block which is used by an Roo.data.Store object as
6258      * a cache of Roo.data.Records.
6259      */
6260     readRecords : function(o){
6261         /**
6262          * After any data loads, the raw JSON data is available for further custom processing.
6263          * @type Object
6264          */
6265         this.o = o;
6266         var s = this.meta, Record = this.recordType,
6267             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6268
6269 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6270         if (!this.ef) {
6271             if(s.totalProperty) {
6272                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6273                 }
6274                 if(s.successProperty) {
6275                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6276                 }
6277                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6278                 if (s.id) {
6279                         var g = this.getJsonAccessor(s.id);
6280                         this.getId = function(rec) {
6281                                 var r = g(rec);  
6282                                 return (r === undefined || r === "") ? null : r;
6283                         };
6284                 } else {
6285                         this.getId = function(){return null;};
6286                 }
6287             this.ef = [];
6288             for(var jj = 0; jj < fl; jj++){
6289                 f = fi[jj];
6290                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6291                 this.ef[jj] = this.getJsonAccessor(map);
6292             }
6293         }
6294
6295         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6296         if(s.totalProperty){
6297             var vt = parseInt(this.getTotal(o), 10);
6298             if(!isNaN(vt)){
6299                 totalRecords = vt;
6300             }
6301         }
6302         if(s.successProperty){
6303             var vs = this.getSuccess(o);
6304             if(vs === false || vs === 'false'){
6305                 success = false;
6306             }
6307         }
6308         var records = [];
6309         for(var i = 0; i < c; i++){
6310                 var n = root[i];
6311             var values = {};
6312             var id = this.getId(n);
6313             for(var j = 0; j < fl; j++){
6314                 f = fi[j];
6315             var v = this.ef[j](n);
6316             if (!f.convert) {
6317                 Roo.log('missing convert for ' + f.name);
6318                 Roo.log(f);
6319                 continue;
6320             }
6321             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6322             }
6323             var record = new Record(values, id);
6324             record.json = n;
6325             records[i] = record;
6326         }
6327         return {
6328             raw : o,
6329             success : success,
6330             records : records,
6331             totalRecords : totalRecords
6332         };
6333     }
6334 });/*
6335  * Based on:
6336  * Ext JS Library 1.1.1
6337  * Copyright(c) 2006-2007, Ext JS, LLC.
6338  *
6339  * Originally Released Under LGPL - original licence link has changed is not relivant.
6340  *
6341  * Fork - LGPL
6342  * <script type="text/javascript">
6343  */
6344
6345 /**
6346  * @class Roo.data.XmlReader
6347  * @extends Roo.data.DataReader
6348  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6349  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6350  * <p>
6351  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6352  * header in the HTTP response must be set to "text/xml".</em>
6353  * <p>
6354  * Example code:
6355  * <pre><code>
6356 var RecordDef = Roo.data.Record.create([
6357    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6358    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6359 ]);
6360 var myReader = new Roo.data.XmlReader({
6361    totalRecords: "results", // The element which contains the total dataset size (optional)
6362    record: "row",           // The repeated element which contains row information
6363    id: "id"                 // The element within the row that provides an ID for the record (optional)
6364 }, RecordDef);
6365 </code></pre>
6366  * <p>
6367  * This would consume an XML file like this:
6368  * <pre><code>
6369 &lt;?xml?>
6370 &lt;dataset>
6371  &lt;results>2&lt;/results>
6372  &lt;row>
6373    &lt;id>1&lt;/id>
6374    &lt;name>Bill&lt;/name>
6375    &lt;occupation>Gardener&lt;/occupation>
6376  &lt;/row>
6377  &lt;row>
6378    &lt;id>2&lt;/id>
6379    &lt;name>Ben&lt;/name>
6380    &lt;occupation>Horticulturalist&lt;/occupation>
6381  &lt;/row>
6382 &lt;/dataset>
6383 </code></pre>
6384  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6385  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6386  * paged from the remote server.
6387  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6388  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6389  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6390  * a record identifier value.
6391  * @constructor
6392  * Create a new XmlReader
6393  * @param {Object} meta Metadata configuration options
6394  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6395  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6396  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6397  */
6398 Roo.data.XmlReader = function(meta, recordType){
6399     meta = meta || {};
6400     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6401 };
6402 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6403     /**
6404      * This method is only used by a DataProxy which has retrieved data from a remote server.
6405          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6406          * to contain a method called 'responseXML' that returns an XML document object.
6407      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6408      * a cache of Roo.data.Records.
6409      */
6410     read : function(response){
6411         var doc = response.responseXML;
6412         if(!doc) {
6413             throw {message: "XmlReader.read: XML Document not available"};
6414         }
6415         return this.readRecords(doc);
6416     },
6417
6418     /**
6419      * Create a data block containing Roo.data.Records from an XML document.
6420          * @param {Object} doc A parsed XML document.
6421      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6422      * a cache of Roo.data.Records.
6423      */
6424     readRecords : function(doc){
6425         /**
6426          * After any data loads/reads, the raw XML Document is available for further custom processing.
6427          * @type XMLDocument
6428          */
6429         this.xmlData = doc;
6430         var root = doc.documentElement || doc;
6431         var q = Roo.DomQuery;
6432         var recordType = this.recordType, fields = recordType.prototype.fields;
6433         var sid = this.meta.id;
6434         var totalRecords = 0, success = true;
6435         if(this.meta.totalRecords){
6436             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6437         }
6438         
6439         if(this.meta.success){
6440             var sv = q.selectValue(this.meta.success, root, true);
6441             success = sv !== false && sv !== 'false';
6442         }
6443         var records = [];
6444         var ns = q.select(this.meta.record, root);
6445         for(var i = 0, len = ns.length; i < len; i++) {
6446                 var n = ns[i];
6447                 var values = {};
6448                 var id = sid ? q.selectValue(sid, n) : undefined;
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                     var f = fields.items[j];
6451                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6452                     v = f.convert(v);
6453                     values[f.name] = v;
6454                 }
6455                 var record = new recordType(values, id);
6456                 record.node = n;
6457                 records[records.length] = record;
6458             }
6459
6460             return {
6461                 success : success,
6462                 records : records,
6463                 totalRecords : totalRecords || records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477 /**
6478  * @class Roo.data.ArrayReader
6479  * @extends Roo.data.DataReader
6480  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6481  * Each element of that Array represents a row of data fields. The
6482  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6483  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6484  * <p>
6485  * Example code:.
6486  * <pre><code>
6487 var RecordDef = Roo.data.Record.create([
6488     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6489     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6490 ]);
6491 var myReader = new Roo.data.ArrayReader({
6492     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6493 }, RecordDef);
6494 </code></pre>
6495  * <p>
6496  * This would consume an Array like this:
6497  * <pre><code>
6498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6499   </code></pre>
6500  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6501  * @constructor
6502  * Create a new JsonReader
6503  * @param {Object} meta Metadata configuration options.
6504  * @param {Object} recordType Either an Array of field definition objects
6505  * as specified to {@link Roo.data.Record#create},
6506  * or an {@link Roo.data.Record} object
6507  * created using {@link Roo.data.Record#create}.
6508  */
6509 Roo.data.ArrayReader = function(meta, recordType){
6510     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6511 };
6512
6513 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6514     /**
6515      * Create a data block containing Roo.data.Records from an XML document.
6516      * @param {Object} o An Array of row objects which represents the dataset.
6517      * @return {Object} data A data block which is used by an Roo.data.Store object as
6518      * a cache of Roo.data.Records.
6519      */
6520     readRecords : function(o){
6521         var sid = this.meta ? this.meta.id : null;
6522         var recordType = this.recordType, fields = recordType.prototype.fields;
6523         var records = [];
6524         var root = o;
6525             for(var i = 0; i < root.length; i++){
6526                     var n = root[i];
6527                 var values = {};
6528                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6529                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6530                 var f = fields.items[j];
6531                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6532                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6533                 v = f.convert(v);
6534                 values[f.name] = v;
6535             }
6536                 var record = new recordType(values, id);
6537                 record.json = n;
6538                 records[records.length] = record;
6539             }
6540             return {
6541                 records : records,
6542                 totalRecords : records.length
6543             };
6544     }
6545 });/*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555
6556
6557 /**
6558  * @class Roo.data.Tree
6559  * @extends Roo.util.Observable
6560  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6561  * in the tree have most standard DOM functionality.
6562  * @constructor
6563  * @param {Node} root (optional) The root node
6564  */
6565 Roo.data.Tree = function(root){
6566    this.nodeHash = {};
6567    /**
6568     * The root node for this tree
6569     * @type Node
6570     */
6571    this.root = null;
6572    if(root){
6573        this.setRootNode(root);
6574    }
6575    this.addEvents({
6576        /**
6577         * @event append
6578         * Fires when a new child node is appended to a node in this tree.
6579         * @param {Tree} tree The owner tree
6580         * @param {Node} parent The parent node
6581         * @param {Node} node The newly appended node
6582         * @param {Number} index The index of the newly appended node
6583         */
6584        "append" : true,
6585        /**
6586         * @event remove
6587         * Fires when a child node is removed from a node in this tree.
6588         * @param {Tree} tree The owner tree
6589         * @param {Node} parent The parent node
6590         * @param {Node} node The child node removed
6591         */
6592        "remove" : true,
6593        /**
6594         * @event move
6595         * Fires when a node is moved to a new location in the tree
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} node The node moved
6598         * @param {Node} oldParent The old parent of this node
6599         * @param {Node} newParent The new parent of this node
6600         * @param {Number} index The index it was moved to
6601         */
6602        "move" : true,
6603        /**
6604         * @event insert
6605         * Fires when a new child node is inserted in a node in this tree.
6606         * @param {Tree} tree The owner tree
6607         * @param {Node} parent The parent node
6608         * @param {Node} node The child node inserted
6609         * @param {Node} refNode The child node the node was inserted before
6610         */
6611        "insert" : true,
6612        /**
6613         * @event beforeappend
6614         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} parent The parent node
6617         * @param {Node} node The child node to be appended
6618         */
6619        "beforeappend" : true,
6620        /**
6621         * @event beforeremove
6622         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6623         * @param {Tree} tree The owner tree
6624         * @param {Node} parent The parent node
6625         * @param {Node} node The child node to be removed
6626         */
6627        "beforeremove" : true,
6628        /**
6629         * @event beforemove
6630         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6631         * @param {Tree} tree The owner tree
6632         * @param {Node} node The node being moved
6633         * @param {Node} oldParent The parent of the node
6634         * @param {Node} newParent The new parent the node is moving to
6635         * @param {Number} index The index it is being moved to
6636         */
6637        "beforemove" : true,
6638        /**
6639         * @event beforeinsert
6640         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6641         * @param {Tree} tree The owner tree
6642         * @param {Node} parent The parent node
6643         * @param {Node} node The child node to be inserted
6644         * @param {Node} refNode The child node the node is being inserted before
6645         */
6646        "beforeinsert" : true
6647    });
6648
6649     Roo.data.Tree.superclass.constructor.call(this);
6650 };
6651
6652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6653     pathSeparator: "/",
6654
6655     proxyNodeEvent : function(){
6656         return this.fireEvent.apply(this, arguments);
6657     },
6658
6659     /**
6660      * Returns the root node for this tree.
6661      * @return {Node}
6662      */
6663     getRootNode : function(){
6664         return this.root;
6665     },
6666
6667     /**
6668      * Sets the root node for this tree.
6669      * @param {Node} node
6670      * @return {Node}
6671      */
6672     setRootNode : function(node){
6673         this.root = node;
6674         node.ownerTree = this;
6675         node.isRoot = true;
6676         this.registerNode(node);
6677         return node;
6678     },
6679
6680     /**
6681      * Gets a node in this tree by its id.
6682      * @param {String} id
6683      * @return {Node}
6684      */
6685     getNodeById : function(id){
6686         return this.nodeHash[id];
6687     },
6688
6689     registerNode : function(node){
6690         this.nodeHash[node.id] = node;
6691     },
6692
6693     unregisterNode : function(node){
6694         delete this.nodeHash[node.id];
6695     },
6696
6697     toString : function(){
6698         return "[Tree"+(this.id?" "+this.id:"")+"]";
6699     }
6700 });
6701
6702 /**
6703  * @class Roo.data.Node
6704  * @extends Roo.util.Observable
6705  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6706  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6707  * @constructor
6708  * @param {Object} attributes The attributes/config for the node
6709  */
6710 Roo.data.Node = function(attributes){
6711     /**
6712      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6713      * @type {Object}
6714      */
6715     this.attributes = attributes || {};
6716     this.leaf = this.attributes.leaf;
6717     /**
6718      * The node id. @type String
6719      */
6720     this.id = this.attributes.id;
6721     if(!this.id){
6722         this.id = Roo.id(null, "ynode-");
6723         this.attributes.id = this.id;
6724     }
6725      
6726     
6727     /**
6728      * All child nodes of this node. @type Array
6729      */
6730     this.childNodes = [];
6731     if(!this.childNodes.indexOf){ // indexOf is a must
6732         this.childNodes.indexOf = function(o){
6733             for(var i = 0, len = this.length; i < len; i++){
6734                 if(this[i] == o) {
6735                     return i;
6736                 }
6737             }
6738             return -1;
6739         };
6740     }
6741     /**
6742      * The parent node for this node. @type Node
6743      */
6744     this.parentNode = null;
6745     /**
6746      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6747      */
6748     this.firstChild = null;
6749     /**
6750      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6751      */
6752     this.lastChild = null;
6753     /**
6754      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6755      */
6756     this.previousSibling = null;
6757     /**
6758      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6759      */
6760     this.nextSibling = null;
6761
6762     this.addEvents({
6763        /**
6764         * @event append
6765         * Fires when a new child node is appended
6766         * @param {Tree} tree The owner tree
6767         * @param {Node} this This node
6768         * @param {Node} node The newly appended node
6769         * @param {Number} index The index of the newly appended node
6770         */
6771        "append" : true,
6772        /**
6773         * @event remove
6774         * Fires when a child node is removed
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The removed node
6778         */
6779        "remove" : true,
6780        /**
6781         * @event move
6782         * Fires when this node is moved to a new location in the tree
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} oldParent The old parent of this node
6786         * @param {Node} newParent The new parent of this node
6787         * @param {Number} index The index it was moved to
6788         */
6789        "move" : true,
6790        /**
6791         * @event insert
6792         * Fires when a new child node is inserted.
6793         * @param {Tree} tree The owner tree
6794         * @param {Node} this This node
6795         * @param {Node} node The child node inserted
6796         * @param {Node} refNode The child node the node was inserted before
6797         */
6798        "insert" : true,
6799        /**
6800         * @event beforeappend
6801         * Fires before a new child is appended, return false to cancel the append.
6802         * @param {Tree} tree The owner tree
6803         * @param {Node} this This node
6804         * @param {Node} node The child node to be appended
6805         */
6806        "beforeappend" : true,
6807        /**
6808         * @event beforeremove
6809         * Fires before a child is removed, return false to cancel the remove.
6810         * @param {Tree} tree The owner tree
6811         * @param {Node} this This node
6812         * @param {Node} node The child node to be removed
6813         */
6814        "beforeremove" : true,
6815        /**
6816         * @event beforemove
6817         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6818         * @param {Tree} tree The owner tree
6819         * @param {Node} this This node
6820         * @param {Node} oldParent The parent of this node
6821         * @param {Node} newParent The new parent this node is moving to
6822         * @param {Number} index The index it is being moved to
6823         */
6824        "beforemove" : true,
6825        /**
6826         * @event beforeinsert
6827         * Fires before a new child is inserted, return false to cancel the insert.
6828         * @param {Tree} tree The owner tree
6829         * @param {Node} this This node
6830         * @param {Node} node The child node to be inserted
6831         * @param {Node} refNode The child node the node is being inserted before
6832         */
6833        "beforeinsert" : true
6834    });
6835     this.listeners = this.attributes.listeners;
6836     Roo.data.Node.superclass.constructor.call(this);
6837 };
6838
6839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6840     fireEvent : function(evtName){
6841         // first do standard event for this node
6842         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6843             return false;
6844         }
6845         // then bubble it up to the tree if the event wasn't cancelled
6846         var ot = this.getOwnerTree();
6847         if(ot){
6848             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6849                 return false;
6850             }
6851         }
6852         return true;
6853     },
6854
6855     /**
6856      * Returns true if this node is a leaf
6857      * @return {Boolean}
6858      */
6859     isLeaf : function(){
6860         return this.leaf === true;
6861     },
6862
6863     // private
6864     setFirstChild : function(node){
6865         this.firstChild = node;
6866     },
6867
6868     //private
6869     setLastChild : function(node){
6870         this.lastChild = node;
6871     },
6872
6873
6874     /**
6875      * Returns true if this node is the last child of its parent
6876      * @return {Boolean}
6877      */
6878     isLast : function(){
6879        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6880     },
6881
6882     /**
6883      * Returns true if this node is the first child of its parent
6884      * @return {Boolean}
6885      */
6886     isFirst : function(){
6887        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6888     },
6889
6890     hasChildNodes : function(){
6891         return !this.isLeaf() && this.childNodes.length > 0;
6892     },
6893
6894     /**
6895      * Insert node(s) as the last child node of this node.
6896      * @param {Node/Array} node The node or Array of nodes to append
6897      * @return {Node} The appended node if single append, or null if an array was passed
6898      */
6899     appendChild : function(node){
6900         var multi = false;
6901         if(node instanceof Array){
6902             multi = node;
6903         }else if(arguments.length > 1){
6904             multi = arguments;
6905         }
6906         // if passed an array or multiple args do them one by one
6907         if(multi){
6908             for(var i = 0, len = multi.length; i < len; i++) {
6909                 this.appendChild(multi[i]);
6910             }
6911         }else{
6912             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6913                 return false;
6914             }
6915             var index = this.childNodes.length;
6916             var oldParent = node.parentNode;
6917             // it's a move, make sure we move it cleanly
6918             if(oldParent){
6919                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6920                     return false;
6921                 }
6922                 oldParent.removeChild(node);
6923             }
6924             index = this.childNodes.length;
6925             if(index == 0){
6926                 this.setFirstChild(node);
6927             }
6928             this.childNodes.push(node);
6929             node.parentNode = this;
6930             var ps = this.childNodes[index-1];
6931             if(ps){
6932                 node.previousSibling = ps;
6933                 ps.nextSibling = node;
6934             }else{
6935                 node.previousSibling = null;
6936             }
6937             node.nextSibling = null;
6938             this.setLastChild(node);
6939             node.setOwnerTree(this.getOwnerTree());
6940             this.fireEvent("append", this.ownerTree, this, node, index);
6941             if(oldParent){
6942                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6943             }
6944             return node;
6945         }
6946     },
6947
6948     /**
6949      * Removes a child node from this node.
6950      * @param {Node} node The node to remove
6951      * @return {Node} The removed node
6952      */
6953     removeChild : function(node){
6954         var index = this.childNodes.indexOf(node);
6955         if(index == -1){
6956             return false;
6957         }
6958         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6959             return false;
6960         }
6961
6962         // remove it from childNodes collection
6963         this.childNodes.splice(index, 1);
6964
6965         // update siblings
6966         if(node.previousSibling){
6967             node.previousSibling.nextSibling = node.nextSibling;
6968         }
6969         if(node.nextSibling){
6970             node.nextSibling.previousSibling = node.previousSibling;
6971         }
6972
6973         // update child refs
6974         if(this.firstChild == node){
6975             this.setFirstChild(node.nextSibling);
6976         }
6977         if(this.lastChild == node){
6978             this.setLastChild(node.previousSibling);
6979         }
6980
6981         node.setOwnerTree(null);
6982         // clear any references from the node
6983         node.parentNode = null;
6984         node.previousSibling = null;
6985         node.nextSibling = null;
6986         this.fireEvent("remove", this.ownerTree, this, node);
6987         return node;
6988     },
6989
6990     /**
6991      * Inserts the first node before the second node in this nodes childNodes collection.
6992      * @param {Node} node The node to insert
6993      * @param {Node} refNode The node to insert before (if null the node is appended)
6994      * @return {Node} The inserted node
6995      */
6996     insertBefore : function(node, refNode){
6997         if(!refNode){ // like standard Dom, refNode can be null for append
6998             return this.appendChild(node);
6999         }
7000         // nothing to do
7001         if(node == refNode){
7002             return false;
7003         }
7004
7005         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7006             return false;
7007         }
7008         var index = this.childNodes.indexOf(refNode);
7009         var oldParent = node.parentNode;
7010         var refIndex = index;
7011
7012         // when moving internally, indexes will change after remove
7013         if(oldParent == this && this.childNodes.indexOf(node) < index){
7014             refIndex--;
7015         }
7016
7017         // it's a move, make sure we move it cleanly
7018         if(oldParent){
7019             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7020                 return false;
7021             }
7022             oldParent.removeChild(node);
7023         }
7024         if(refIndex == 0){
7025             this.setFirstChild(node);
7026         }
7027         this.childNodes.splice(refIndex, 0, node);
7028         node.parentNode = this;
7029         var ps = this.childNodes[refIndex-1];
7030         if(ps){
7031             node.previousSibling = ps;
7032             ps.nextSibling = node;
7033         }else{
7034             node.previousSibling = null;
7035         }
7036         node.nextSibling = refNode;
7037         refNode.previousSibling = node;
7038         node.setOwnerTree(this.getOwnerTree());
7039         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7040         if(oldParent){
7041             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7042         }
7043         return node;
7044     },
7045
7046     /**
7047      * Returns the child node at the specified index.
7048      * @param {Number} index
7049      * @return {Node}
7050      */
7051     item : function(index){
7052         return this.childNodes[index];
7053     },
7054
7055     /**
7056      * Replaces one child node in this node with another.
7057      * @param {Node} newChild The replacement node
7058      * @param {Node} oldChild The node to replace
7059      * @return {Node} The replaced node
7060      */
7061     replaceChild : function(newChild, oldChild){
7062         this.insertBefore(newChild, oldChild);
7063         this.removeChild(oldChild);
7064         return oldChild;
7065     },
7066
7067     /**
7068      * Returns the index of a child node
7069      * @param {Node} node
7070      * @return {Number} The index of the node or -1 if it was not found
7071      */
7072     indexOf : function(child){
7073         return this.childNodes.indexOf(child);
7074     },
7075
7076     /**
7077      * Returns the tree this node is in.
7078      * @return {Tree}
7079      */
7080     getOwnerTree : function(){
7081         // if it doesn't have one, look for one
7082         if(!this.ownerTree){
7083             var p = this;
7084             while(p){
7085                 if(p.ownerTree){
7086                     this.ownerTree = p.ownerTree;
7087                     break;
7088                 }
7089                 p = p.parentNode;
7090             }
7091         }
7092         return this.ownerTree;
7093     },
7094
7095     /**
7096      * Returns depth of this node (the root node has a depth of 0)
7097      * @return {Number}
7098      */
7099     getDepth : function(){
7100         var depth = 0;
7101         var p = this;
7102         while(p.parentNode){
7103             ++depth;
7104             p = p.parentNode;
7105         }
7106         return depth;
7107     },
7108
7109     // private
7110     setOwnerTree : function(tree){
7111         // if it's move, we need to update everyone
7112         if(tree != this.ownerTree){
7113             if(this.ownerTree){
7114                 this.ownerTree.unregisterNode(this);
7115             }
7116             this.ownerTree = tree;
7117             var cs = this.childNodes;
7118             for(var i = 0, len = cs.length; i < len; i++) {
7119                 cs[i].setOwnerTree(tree);
7120             }
7121             if(tree){
7122                 tree.registerNode(this);
7123             }
7124         }
7125     },
7126
7127     /**
7128      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7129      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7130      * @return {String} The path
7131      */
7132     getPath : function(attr){
7133         attr = attr || "id";
7134         var p = this.parentNode;
7135         var b = [this.attributes[attr]];
7136         while(p){
7137             b.unshift(p.attributes[attr]);
7138             p = p.parentNode;
7139         }
7140         var sep = this.getOwnerTree().pathSeparator;
7141         return sep + b.join(sep);
7142     },
7143
7144     /**
7145      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7146      * function call will be the scope provided or the current node. The arguments to the function
7147      * will be the args provided or the current node. If the function returns false at any point,
7148      * the bubble is stopped.
7149      * @param {Function} fn The function to call
7150      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7151      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7152      */
7153     bubble : function(fn, scope, args){
7154         var p = this;
7155         while(p){
7156             if(fn.call(scope || p, args || p) === false){
7157                 break;
7158             }
7159             p = p.parentNode;
7160         }
7161     },
7162
7163     /**
7164      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7165      * function call will be the scope provided or the current node. The arguments to the function
7166      * will be the args provided or the current node. If the function returns false at any point,
7167      * the cascade is stopped on that branch.
7168      * @param {Function} fn The function to call
7169      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7170      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7171      */
7172     cascade : function(fn, scope, args){
7173         if(fn.call(scope || this, args || this) !== false){
7174             var cs = this.childNodes;
7175             for(var i = 0, len = cs.length; i < len; i++) {
7176                 cs[i].cascade(fn, scope, args);
7177             }
7178         }
7179     },
7180
7181     /**
7182      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7183      * function call will be the scope provided or the current node. The arguments to the function
7184      * will be the args provided or the current node. If the function returns false at any point,
7185      * the iteration stops.
7186      * @param {Function} fn The function to call
7187      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7188      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7189      */
7190     eachChild : function(fn, scope, args){
7191         var cs = this.childNodes;
7192         for(var i = 0, len = cs.length; i < len; i++) {
7193                 if(fn.call(scope || this, args || cs[i]) === false){
7194                     break;
7195                 }
7196         }
7197     },
7198
7199     /**
7200      * Finds the first child that has the attribute with the specified value.
7201      * @param {String} attribute The attribute name
7202      * @param {Mixed} value The value to search for
7203      * @return {Node} The found child or null if none was found
7204      */
7205     findChild : function(attribute, value){
7206         var cs = this.childNodes;
7207         for(var i = 0, len = cs.length; i < len; i++) {
7208                 if(cs[i].attributes[attribute] == value){
7209                     return cs[i];
7210                 }
7211         }
7212         return null;
7213     },
7214
7215     /**
7216      * Finds the first child by a custom function. The child matches if the function passed
7217      * returns true.
7218      * @param {Function} fn
7219      * @param {Object} scope (optional)
7220      * @return {Node} The found child or null if none was found
7221      */
7222     findChildBy : function(fn, scope){
7223         var cs = this.childNodes;
7224         for(var i = 0, len = cs.length; i < len; i++) {
7225                 if(fn.call(scope||cs[i], cs[i]) === true){
7226                     return cs[i];
7227                 }
7228         }
7229         return null;
7230     },
7231
7232     /**
7233      * Sorts this nodes children using the supplied sort function
7234      * @param {Function} fn
7235      * @param {Object} scope (optional)
7236      */
7237     sort : function(fn, scope){
7238         var cs = this.childNodes;
7239         var len = cs.length;
7240         if(len > 0){
7241             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7242             cs.sort(sortFn);
7243             for(var i = 0; i < len; i++){
7244                 var n = cs[i];
7245                 n.previousSibling = cs[i-1];
7246                 n.nextSibling = cs[i+1];
7247                 if(i == 0){
7248                     this.setFirstChild(n);
7249                 }
7250                 if(i == len-1){
7251                     this.setLastChild(n);
7252                 }
7253             }
7254         }
7255     },
7256
7257     /**
7258      * Returns true if this node is an ancestor (at any point) of the passed node.
7259      * @param {Node} node
7260      * @return {Boolean}
7261      */
7262     contains : function(node){
7263         return node.isAncestor(this);
7264     },
7265
7266     /**
7267      * Returns true if the passed node is an ancestor (at any point) of this node.
7268      * @param {Node} node
7269      * @return {Boolean}
7270      */
7271     isAncestor : function(node){
7272         var p = this.parentNode;
7273         while(p){
7274             if(p == node){
7275                 return true;
7276             }
7277             p = p.parentNode;
7278         }
7279         return false;
7280     },
7281
7282     toString : function(){
7283         return "[Node"+(this.id?" "+this.id:"")+"]";
7284     }
7285 });/*
7286  * Based on:
7287  * Ext JS Library 1.1.1
7288  * Copyright(c) 2006-2007, Ext JS, LLC.
7289  *
7290  * Originally Released Under LGPL - original licence link has changed is not relivant.
7291  *
7292  * Fork - LGPL
7293  * <script type="text/javascript">
7294  */
7295  (function(){ 
7296 /**
7297  * @class Roo.Layer
7298  * @extends Roo.Element
7299  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7300  * automatic maintaining of shadow/shim positions.
7301  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7302  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7303  * you can pass a string with a CSS class name. False turns off the shadow.
7304  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7305  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7306  * @cfg {String} cls CSS class to add to the element
7307  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7308  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7309  * @constructor
7310  * @param {Object} config An object with config options.
7311  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7312  */
7313
7314 Roo.Layer = function(config, existingEl){
7315     config = config || {};
7316     var dh = Roo.DomHelper;
7317     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7318     if(existingEl){
7319         this.dom = Roo.getDom(existingEl);
7320     }
7321     if(!this.dom){
7322         var o = config.dh || {tag: "div", cls: "x-layer"};
7323         this.dom = dh.append(pel, o);
7324     }
7325     if(config.cls){
7326         this.addClass(config.cls);
7327     }
7328     this.constrain = config.constrain !== false;
7329     this.visibilityMode = Roo.Element.VISIBILITY;
7330     if(config.id){
7331         this.id = this.dom.id = config.id;
7332     }else{
7333         this.id = Roo.id(this.dom);
7334     }
7335     this.zindex = config.zindex || this.getZIndex();
7336     this.position("absolute", this.zindex);
7337     if(config.shadow){
7338         this.shadowOffset = config.shadowOffset || 4;
7339         this.shadow = new Roo.Shadow({
7340             offset : this.shadowOffset,
7341             mode : config.shadow
7342         });
7343     }else{
7344         this.shadowOffset = 0;
7345     }
7346     this.useShim = config.shim !== false && Roo.useShims;
7347     this.useDisplay = config.useDisplay;
7348     this.hide();
7349 };
7350
7351 var supr = Roo.Element.prototype;
7352
7353 // shims are shared among layer to keep from having 100 iframes
7354 var shims = [];
7355
7356 Roo.extend(Roo.Layer, Roo.Element, {
7357
7358     getZIndex : function(){
7359         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7360     },
7361
7362     getShim : function(){
7363         if(!this.useShim){
7364             return null;
7365         }
7366         if(this.shim){
7367             return this.shim;
7368         }
7369         var shim = shims.shift();
7370         if(!shim){
7371             shim = this.createShim();
7372             shim.enableDisplayMode('block');
7373             shim.dom.style.display = 'none';
7374             shim.dom.style.visibility = 'visible';
7375         }
7376         var pn = this.dom.parentNode;
7377         if(shim.dom.parentNode != pn){
7378             pn.insertBefore(shim.dom, this.dom);
7379         }
7380         shim.setStyle('z-index', this.getZIndex()-2);
7381         this.shim = shim;
7382         return shim;
7383     },
7384
7385     hideShim : function(){
7386         if(this.shim){
7387             this.shim.setDisplayed(false);
7388             shims.push(this.shim);
7389             delete this.shim;
7390         }
7391     },
7392
7393     disableShadow : function(){
7394         if(this.shadow){
7395             this.shadowDisabled = true;
7396             this.shadow.hide();
7397             this.lastShadowOffset = this.shadowOffset;
7398             this.shadowOffset = 0;
7399         }
7400     },
7401
7402     enableShadow : function(show){
7403         if(this.shadow){
7404             this.shadowDisabled = false;
7405             this.shadowOffset = this.lastShadowOffset;
7406             delete this.lastShadowOffset;
7407             if(show){
7408                 this.sync(true);
7409             }
7410         }
7411     },
7412
7413     // private
7414     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7415     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7416     sync : function(doShow){
7417         var sw = this.shadow;
7418         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7419             var sh = this.getShim();
7420
7421             var w = this.getWidth(),
7422                 h = this.getHeight();
7423
7424             var l = this.getLeft(true),
7425                 t = this.getTop(true);
7426
7427             if(sw && !this.shadowDisabled){
7428                 if(doShow && !sw.isVisible()){
7429                     sw.show(this);
7430                 }else{
7431                     sw.realign(l, t, w, h);
7432                 }
7433                 if(sh){
7434                     if(doShow){
7435                        sh.show();
7436                     }
7437                     // fit the shim behind the shadow, so it is shimmed too
7438                     var a = sw.adjusts, s = sh.dom.style;
7439                     s.left = (Math.min(l, l+a.l))+"px";
7440                     s.top = (Math.min(t, t+a.t))+"px";
7441                     s.width = (w+a.w)+"px";
7442                     s.height = (h+a.h)+"px";
7443                 }
7444             }else if(sh){
7445                 if(doShow){
7446                    sh.show();
7447                 }
7448                 sh.setSize(w, h);
7449                 sh.setLeftTop(l, t);
7450             }
7451             
7452         }
7453     },
7454
7455     // private
7456     destroy : function(){
7457         this.hideShim();
7458         if(this.shadow){
7459             this.shadow.hide();
7460         }
7461         this.removeAllListeners();
7462         var pn = this.dom.parentNode;
7463         if(pn){
7464             pn.removeChild(this.dom);
7465         }
7466         Roo.Element.uncache(this.id);
7467     },
7468
7469     remove : function(){
7470         this.destroy();
7471     },
7472
7473     // private
7474     beginUpdate : function(){
7475         this.updating = true;
7476     },
7477
7478     // private
7479     endUpdate : function(){
7480         this.updating = false;
7481         this.sync(true);
7482     },
7483
7484     // private
7485     hideUnders : function(negOffset){
7486         if(this.shadow){
7487             this.shadow.hide();
7488         }
7489         this.hideShim();
7490     },
7491
7492     // private
7493     constrainXY : function(){
7494         if(this.constrain){
7495             var vw = Roo.lib.Dom.getViewWidth(),
7496                 vh = Roo.lib.Dom.getViewHeight();
7497             var s = Roo.get(document).getScroll();
7498
7499             var xy = this.getXY();
7500             var x = xy[0], y = xy[1];   
7501             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7502             // only move it if it needs it
7503             var moved = false;
7504             // first validate right/bottom
7505             if((x + w) > vw+s.left){
7506                 x = vw - w - this.shadowOffset;
7507                 moved = true;
7508             }
7509             if((y + h) > vh+s.top){
7510                 y = vh - h - this.shadowOffset;
7511                 moved = true;
7512             }
7513             // then make sure top/left isn't negative
7514             if(x < s.left){
7515                 x = s.left;
7516                 moved = true;
7517             }
7518             if(y < s.top){
7519                 y = s.top;
7520                 moved = true;
7521             }
7522             if(moved){
7523                 if(this.avoidY){
7524                     var ay = this.avoidY;
7525                     if(y <= ay && (y+h) >= ay){
7526                         y = ay-h-5;   
7527                     }
7528                 }
7529                 xy = [x, y];
7530                 this.storeXY(xy);
7531                 supr.setXY.call(this, xy);
7532                 this.sync();
7533             }
7534         }
7535     },
7536
7537     isVisible : function(){
7538         return this.visible;    
7539     },
7540
7541     // private
7542     showAction : function(){
7543         this.visible = true; // track visibility to prevent getStyle calls
7544         if(this.useDisplay === true){
7545             this.setDisplayed("");
7546         }else if(this.lastXY){
7547             supr.setXY.call(this, this.lastXY);
7548         }else if(this.lastLT){
7549             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7550         }
7551     },
7552
7553     // private
7554     hideAction : function(){
7555         this.visible = false;
7556         if(this.useDisplay === true){
7557             this.setDisplayed(false);
7558         }else{
7559             this.setLeftTop(-10000,-10000);
7560         }
7561     },
7562
7563     // overridden Element method
7564     setVisible : function(v, a, d, c, e){
7565         if(v){
7566             this.showAction();
7567         }
7568         if(a && v){
7569             var cb = function(){
7570                 this.sync(true);
7571                 if(c){
7572                     c();
7573                 }
7574             }.createDelegate(this);
7575             supr.setVisible.call(this, true, true, d, cb, e);
7576         }else{
7577             if(!v){
7578                 this.hideUnders(true);
7579             }
7580             var cb = c;
7581             if(a){
7582                 cb = function(){
7583                     this.hideAction();
7584                     if(c){
7585                         c();
7586                     }
7587                 }.createDelegate(this);
7588             }
7589             supr.setVisible.call(this, v, a, d, cb, e);
7590             if(v){
7591                 this.sync(true);
7592             }else if(!a){
7593                 this.hideAction();
7594             }
7595         }
7596     },
7597
7598     storeXY : function(xy){
7599         delete this.lastLT;
7600         this.lastXY = xy;
7601     },
7602
7603     storeLeftTop : function(left, top){
7604         delete this.lastXY;
7605         this.lastLT = [left, top];
7606     },
7607
7608     // private
7609     beforeFx : function(){
7610         this.beforeAction();
7611         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7612     },
7613
7614     // private
7615     afterFx : function(){
7616         Roo.Layer.superclass.afterFx.apply(this, arguments);
7617         this.sync(this.isVisible());
7618     },
7619
7620     // private
7621     beforeAction : function(){
7622         if(!this.updating && this.shadow){
7623             this.shadow.hide();
7624         }
7625     },
7626
7627     // overridden Element method
7628     setLeft : function(left){
7629         this.storeLeftTop(left, this.getTop(true));
7630         supr.setLeft.apply(this, arguments);
7631         this.sync();
7632     },
7633
7634     setTop : function(top){
7635         this.storeLeftTop(this.getLeft(true), top);
7636         supr.setTop.apply(this, arguments);
7637         this.sync();
7638     },
7639
7640     setLeftTop : function(left, top){
7641         this.storeLeftTop(left, top);
7642         supr.setLeftTop.apply(this, arguments);
7643         this.sync();
7644     },
7645
7646     setXY : function(xy, a, d, c, e){
7647         this.fixDisplay();
7648         this.beforeAction();
7649         this.storeXY(xy);
7650         var cb = this.createCB(c);
7651         supr.setXY.call(this, xy, a, d, cb, e);
7652         if(!a){
7653             cb();
7654         }
7655     },
7656
7657     // private
7658     createCB : function(c){
7659         var el = this;
7660         return function(){
7661             el.constrainXY();
7662             el.sync(true);
7663             if(c){
7664                 c();
7665             }
7666         };
7667     },
7668
7669     // overridden Element method
7670     setX : function(x, a, d, c, e){
7671         this.setXY([x, this.getY()], a, d, c, e);
7672     },
7673
7674     // overridden Element method
7675     setY : function(y, a, d, c, e){
7676         this.setXY([this.getX(), y], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setSize : function(w, h, a, d, c, e){
7681         this.beforeAction();
7682         var cb = this.createCB(c);
7683         supr.setSize.call(this, w, h, a, d, cb, e);
7684         if(!a){
7685             cb();
7686         }
7687     },
7688
7689     // overridden Element method
7690     setWidth : function(w, a, d, c, e){
7691         this.beforeAction();
7692         var cb = this.createCB(c);
7693         supr.setWidth.call(this, w, a, d, cb, e);
7694         if(!a){
7695             cb();
7696         }
7697     },
7698
7699     // overridden Element method
7700     setHeight : function(h, a, d, c, e){
7701         this.beforeAction();
7702         var cb = this.createCB(c);
7703         supr.setHeight.call(this, h, a, d, cb, e);
7704         if(!a){
7705             cb();
7706         }
7707     },
7708
7709     // overridden Element method
7710     setBounds : function(x, y, w, h, a, d, c, e){
7711         this.beforeAction();
7712         var cb = this.createCB(c);
7713         if(!a){
7714             this.storeXY([x, y]);
7715             supr.setXY.call(this, [x, y]);
7716             supr.setSize.call(this, w, h, a, d, cb, e);
7717             cb();
7718         }else{
7719             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7720         }
7721         return this;
7722     },
7723     
7724     /**
7725      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7726      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7727      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7728      * @param {Number} zindex The new z-index to set
7729      * @return {this} The Layer
7730      */
7731     setZIndex : function(zindex){
7732         this.zindex = zindex;
7733         this.setStyle("z-index", zindex + 2);
7734         if(this.shadow){
7735             this.shadow.setZIndex(zindex + 1);
7736         }
7737         if(this.shim){
7738             this.shim.setStyle("z-index", zindex);
7739         }
7740     }
7741 });
7742 })();/*
7743  * Based on:
7744  * Ext JS Library 1.1.1
7745  * Copyright(c) 2006-2007, Ext JS, LLC.
7746  *
7747  * Originally Released Under LGPL - original licence link has changed is not relivant.
7748  *
7749  * Fork - LGPL
7750  * <script type="text/javascript">
7751  */
7752
7753
7754 /**
7755  * @class Roo.Shadow
7756  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7757  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7758  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7759  * @constructor
7760  * Create a new Shadow
7761  * @param {Object} config The config object
7762  */
7763 Roo.Shadow = function(config){
7764     Roo.apply(this, config);
7765     if(typeof this.mode != "string"){
7766         this.mode = this.defaultMode;
7767     }
7768     var o = this.offset, a = {h: 0};
7769     var rad = Math.floor(this.offset/2);
7770     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7771         case "drop":
7772             a.w = 0;
7773             a.l = a.t = o;
7774             a.t -= 1;
7775             if(Roo.isIE){
7776                 a.l -= this.offset + rad;
7777                 a.t -= this.offset + rad;
7778                 a.w -= rad;
7779                 a.h -= rad;
7780                 a.t += 1;
7781             }
7782         break;
7783         case "sides":
7784             a.w = (o*2);
7785             a.l = -o;
7786             a.t = o-1;
7787             if(Roo.isIE){
7788                 a.l -= (this.offset - rad);
7789                 a.t -= this.offset + rad;
7790                 a.l += 1;
7791                 a.w -= (this.offset - rad)*2;
7792                 a.w -= rad + 1;
7793                 a.h -= 1;
7794             }
7795         break;
7796         case "frame":
7797             a.w = a.h = (o*2);
7798             a.l = a.t = -o;
7799             a.t += 1;
7800             a.h -= 2;
7801             if(Roo.isIE){
7802                 a.l -= (this.offset - rad);
7803                 a.t -= (this.offset - rad);
7804                 a.l += 1;
7805                 a.w -= (this.offset + rad + 1);
7806                 a.h -= (this.offset + rad);
7807                 a.h += 1;
7808             }
7809         break;
7810     };
7811
7812     this.adjusts = a;
7813 };
7814
7815 Roo.Shadow.prototype = {
7816     /**
7817      * @cfg {String} mode
7818      * The shadow display mode.  Supports the following options:<br />
7819      * sides: Shadow displays on both sides and bottom only<br />
7820      * frame: Shadow displays equally on all four sides<br />
7821      * drop: Traditional bottom-right drop shadow (default)
7822      */
7823     /**
7824      * @cfg {String} offset
7825      * The number of pixels to offset the shadow from the element (defaults to 4)
7826      */
7827     offset: 4,
7828
7829     // private
7830     defaultMode: "drop",
7831
7832     /**
7833      * Displays the shadow under the target element
7834      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7835      */
7836     show : function(target){
7837         target = Roo.get(target);
7838         if(!this.el){
7839             this.el = Roo.Shadow.Pool.pull();
7840             if(this.el.dom.nextSibling != target.dom){
7841                 this.el.insertBefore(target);
7842             }
7843         }
7844         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7845         if(Roo.isIE){
7846             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7847         }
7848         this.realign(
7849             target.getLeft(true),
7850             target.getTop(true),
7851             target.getWidth(),
7852             target.getHeight()
7853         );
7854         this.el.dom.style.display = "block";
7855     },
7856
7857     /**
7858      * Returns true if the shadow is visible, else false
7859      */
7860     isVisible : function(){
7861         return this.el ? true : false;  
7862     },
7863
7864     /**
7865      * Direct alignment when values are already available. Show must be called at least once before
7866      * calling this method to ensure it is initialized.
7867      * @param {Number} left The target element left position
7868      * @param {Number} top The target element top position
7869      * @param {Number} width The target element width
7870      * @param {Number} height The target element height
7871      */
7872     realign : function(l, t, w, h){
7873         if(!this.el){
7874             return;
7875         }
7876         var a = this.adjusts, d = this.el.dom, s = d.style;
7877         var iea = 0;
7878         s.left = (l+a.l)+"px";
7879         s.top = (t+a.t)+"px";
7880         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7881  
7882         if(s.width != sws || s.height != shs){
7883             s.width = sws;
7884             s.height = shs;
7885             if(!Roo.isIE){
7886                 var cn = d.childNodes;
7887                 var sww = Math.max(0, (sw-12))+"px";
7888                 cn[0].childNodes[1].style.width = sww;
7889                 cn[1].childNodes[1].style.width = sww;
7890                 cn[2].childNodes[1].style.width = sww;
7891                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7892             }
7893         }
7894     },
7895
7896     /**
7897      * Hides this shadow
7898      */
7899     hide : function(){
7900         if(this.el){
7901             this.el.dom.style.display = "none";
7902             Roo.Shadow.Pool.push(this.el);
7903             delete this.el;
7904         }
7905     },
7906
7907     /**
7908      * Adjust the z-index of this shadow
7909      * @param {Number} zindex The new z-index
7910      */
7911     setZIndex : function(z){
7912         this.zIndex = z;
7913         if(this.el){
7914             this.el.setStyle("z-index", z);
7915         }
7916     }
7917 };
7918
7919 // Private utility class that manages the internal Shadow cache
7920 Roo.Shadow.Pool = function(){
7921     var p = [];
7922     var markup = Roo.isIE ?
7923                  '<div class="x-ie-shadow"></div>' :
7924                  '<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>';
7925     return {
7926         pull : function(){
7927             var sh = p.shift();
7928             if(!sh){
7929                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7930                 sh.autoBoxAdjust = false;
7931             }
7932             return sh;
7933         },
7934
7935         push : function(sh){
7936             p.push(sh);
7937         }
7938     };
7939 }();/*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949
7950
7951 /**
7952  * @class Roo.SplitBar
7953  * @extends Roo.util.Observable
7954  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7955  * <br><br>
7956  * Usage:
7957  * <pre><code>
7958 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7959                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7960 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7961 split.minSize = 100;
7962 split.maxSize = 600;
7963 split.animate = true;
7964 split.on('moved', splitterMoved);
7965 </code></pre>
7966  * @constructor
7967  * Create a new SplitBar
7968  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7969  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7970  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7971  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7972                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7973                         position of the SplitBar).
7974  */
7975 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7976     
7977     /** @private */
7978     this.el = Roo.get(dragElement, true);
7979     this.el.dom.unselectable = "on";
7980     /** @private */
7981     this.resizingEl = Roo.get(resizingElement, true);
7982
7983     /**
7984      * @private
7985      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7986      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7987      * @type Number
7988      */
7989     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7990     
7991     /**
7992      * The minimum size of the resizing element. (Defaults to 0)
7993      * @type Number
7994      */
7995     this.minSize = 0;
7996     
7997     /**
7998      * The maximum size of the resizing element. (Defaults to 2000)
7999      * @type Number
8000      */
8001     this.maxSize = 2000;
8002     
8003     /**
8004      * Whether to animate the transition to the new size
8005      * @type Boolean
8006      */
8007     this.animate = false;
8008     
8009     /**
8010      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8011      * @type Boolean
8012      */
8013     this.useShim = false;
8014     
8015     /** @private */
8016     this.shim = null;
8017     
8018     if(!existingProxy){
8019         /** @private */
8020         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8021     }else{
8022         this.proxy = Roo.get(existingProxy).dom;
8023     }
8024     /** @private */
8025     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8026     
8027     /** @private */
8028     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8029     
8030     /** @private */
8031     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8032     
8033     /** @private */
8034     this.dragSpecs = {};
8035     
8036     /**
8037      * @private The adapter to use to positon and resize elements
8038      */
8039     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8040     this.adapter.init(this);
8041     
8042     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8043         /** @private */
8044         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8045         this.el.addClass("x-splitbar-h");
8046     }else{
8047         /** @private */
8048         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8049         this.el.addClass("x-splitbar-v");
8050     }
8051     
8052     this.addEvents({
8053         /**
8054          * @event resize
8055          * Fires when the splitter is moved (alias for {@link #event-moved})
8056          * @param {Roo.SplitBar} this
8057          * @param {Number} newSize the new width or height
8058          */
8059         "resize" : true,
8060         /**
8061          * @event moved
8062          * Fires when the splitter is moved
8063          * @param {Roo.SplitBar} this
8064          * @param {Number} newSize the new width or height
8065          */
8066         "moved" : true,
8067         /**
8068          * @event beforeresize
8069          * Fires before the splitter is dragged
8070          * @param {Roo.SplitBar} this
8071          */
8072         "beforeresize" : true,
8073
8074         "beforeapply" : true
8075     });
8076
8077     Roo.util.Observable.call(this);
8078 };
8079
8080 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8081     onStartProxyDrag : function(x, y){
8082         this.fireEvent("beforeresize", this);
8083         if(!this.overlay){
8084             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8085             o.unselectable();
8086             o.enableDisplayMode("block");
8087             // all splitbars share the same overlay
8088             Roo.SplitBar.prototype.overlay = o;
8089         }
8090         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8091         this.overlay.show();
8092         Roo.get(this.proxy).setDisplayed("block");
8093         var size = this.adapter.getElementSize(this);
8094         this.activeMinSize = this.getMinimumSize();;
8095         this.activeMaxSize = this.getMaximumSize();;
8096         var c1 = size - this.activeMinSize;
8097         var c2 = Math.max(this.activeMaxSize - size, 0);
8098         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8099             this.dd.resetConstraints();
8100             this.dd.setXConstraint(
8101                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8102                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8103             );
8104             this.dd.setYConstraint(0, 0);
8105         }else{
8106             this.dd.resetConstraints();
8107             this.dd.setXConstraint(0, 0);
8108             this.dd.setYConstraint(
8109                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8110                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8111             );
8112          }
8113         this.dragSpecs.startSize = size;
8114         this.dragSpecs.startPoint = [x, y];
8115         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8116     },
8117     
8118     /** 
8119      * @private Called after the drag operation by the DDProxy
8120      */
8121     onEndProxyDrag : function(e){
8122         Roo.get(this.proxy).setDisplayed(false);
8123         var endPoint = Roo.lib.Event.getXY(e);
8124         if(this.overlay){
8125             this.overlay.hide();
8126         }
8127         var newSize;
8128         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8129             newSize = this.dragSpecs.startSize + 
8130                 (this.placement == Roo.SplitBar.LEFT ?
8131                     endPoint[0] - this.dragSpecs.startPoint[0] :
8132                     this.dragSpecs.startPoint[0] - endPoint[0]
8133                 );
8134         }else{
8135             newSize = this.dragSpecs.startSize + 
8136                 (this.placement == Roo.SplitBar.TOP ?
8137                     endPoint[1] - this.dragSpecs.startPoint[1] :
8138                     this.dragSpecs.startPoint[1] - endPoint[1]
8139                 );
8140         }
8141         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8142         if(newSize != this.dragSpecs.startSize){
8143             if(this.fireEvent('beforeapply', this, newSize) !== false){
8144                 this.adapter.setElementSize(this, newSize);
8145                 this.fireEvent("moved", this, newSize);
8146                 this.fireEvent("resize", this, newSize);
8147             }
8148         }
8149     },
8150     
8151     /**
8152      * Get the adapter this SplitBar uses
8153      * @return The adapter object
8154      */
8155     getAdapter : function(){
8156         return this.adapter;
8157     },
8158     
8159     /**
8160      * Set the adapter this SplitBar uses
8161      * @param {Object} adapter A SplitBar adapter object
8162      */
8163     setAdapter : function(adapter){
8164         this.adapter = adapter;
8165         this.adapter.init(this);
8166     },
8167     
8168     /**
8169      * Gets the minimum size for the resizing element
8170      * @return {Number} The minimum size
8171      */
8172     getMinimumSize : function(){
8173         return this.minSize;
8174     },
8175     
8176     /**
8177      * Sets the minimum size for the resizing element
8178      * @param {Number} minSize The minimum size
8179      */
8180     setMinimumSize : function(minSize){
8181         this.minSize = minSize;
8182     },
8183     
8184     /**
8185      * Gets the maximum size for the resizing element
8186      * @return {Number} The maximum size
8187      */
8188     getMaximumSize : function(){
8189         return this.maxSize;
8190     },
8191     
8192     /**
8193      * Sets the maximum size for the resizing element
8194      * @param {Number} maxSize The maximum size
8195      */
8196     setMaximumSize : function(maxSize){
8197         this.maxSize = maxSize;
8198     },
8199     
8200     /**
8201      * Sets the initialize size for the resizing element
8202      * @param {Number} size The initial size
8203      */
8204     setCurrentSize : function(size){
8205         var oldAnimate = this.animate;
8206         this.animate = false;
8207         this.adapter.setElementSize(this, size);
8208         this.animate = oldAnimate;
8209     },
8210     
8211     /**
8212      * Destroy this splitbar. 
8213      * @param {Boolean} removeEl True to remove the element
8214      */
8215     destroy : function(removeEl){
8216         if(this.shim){
8217             this.shim.remove();
8218         }
8219         this.dd.unreg();
8220         this.proxy.parentNode.removeChild(this.proxy);
8221         if(removeEl){
8222             this.el.remove();
8223         }
8224     }
8225 });
8226
8227 /**
8228  * @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.
8229  */
8230 Roo.SplitBar.createProxy = function(dir){
8231     var proxy = new Roo.Element(document.createElement("div"));
8232     proxy.unselectable();
8233     var cls = 'x-splitbar-proxy';
8234     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8235     document.body.appendChild(proxy.dom);
8236     return proxy.dom;
8237 };
8238
8239 /** 
8240  * @class Roo.SplitBar.BasicLayoutAdapter
8241  * Default Adapter. It assumes the splitter and resizing element are not positioned
8242  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8243  */
8244 Roo.SplitBar.BasicLayoutAdapter = function(){
8245 };
8246
8247 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8248     // do nothing for now
8249     init : function(s){
8250     
8251     },
8252     /**
8253      * Called before drag operations to get the current size of the resizing element. 
8254      * @param {Roo.SplitBar} s The SplitBar using this adapter
8255      */
8256      getElementSize : function(s){
8257         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8258             return s.resizingEl.getWidth();
8259         }else{
8260             return s.resizingEl.getHeight();
8261         }
8262     },
8263     
8264     /**
8265      * Called after drag operations to set the size of the resizing element.
8266      * @param {Roo.SplitBar} s The SplitBar using this adapter
8267      * @param {Number} newSize The new size to set
8268      * @param {Function} onComplete A function to be invoked when resizing is complete
8269      */
8270     setElementSize : function(s, newSize, onComplete){
8271         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8272             if(!s.animate){
8273                 s.resizingEl.setWidth(newSize);
8274                 if(onComplete){
8275                     onComplete(s, newSize);
8276                 }
8277             }else{
8278                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8279             }
8280         }else{
8281             
8282             if(!s.animate){
8283                 s.resizingEl.setHeight(newSize);
8284                 if(onComplete){
8285                     onComplete(s, newSize);
8286                 }
8287             }else{
8288                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8289             }
8290         }
8291     }
8292 };
8293
8294 /** 
8295  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8296  * @extends Roo.SplitBar.BasicLayoutAdapter
8297  * Adapter that  moves the splitter element to align with the resized sizing element. 
8298  * Used with an absolute positioned SplitBar.
8299  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8300  * document.body, make sure you assign an id to the body element.
8301  */
8302 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8303     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8304     this.container = Roo.get(container);
8305 };
8306
8307 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8308     init : function(s){
8309         this.basic.init(s);
8310     },
8311     
8312     getElementSize : function(s){
8313         return this.basic.getElementSize(s);
8314     },
8315     
8316     setElementSize : function(s, newSize, onComplete){
8317         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8318     },
8319     
8320     moveSplitter : function(s){
8321         var yes = Roo.SplitBar;
8322         switch(s.placement){
8323             case yes.LEFT:
8324                 s.el.setX(s.resizingEl.getRight());
8325                 break;
8326             case yes.RIGHT:
8327                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8328                 break;
8329             case yes.TOP:
8330                 s.el.setY(s.resizingEl.getBottom());
8331                 break;
8332             case yes.BOTTOM:
8333                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8334                 break;
8335         }
8336     }
8337 };
8338
8339 /**
8340  * Orientation constant - Create a vertical SplitBar
8341  * @static
8342  * @type Number
8343  */
8344 Roo.SplitBar.VERTICAL = 1;
8345
8346 /**
8347  * Orientation constant - Create a horizontal SplitBar
8348  * @static
8349  * @type Number
8350  */
8351 Roo.SplitBar.HORIZONTAL = 2;
8352
8353 /**
8354  * Placement constant - The resizing element is to the left of the splitter element
8355  * @static
8356  * @type Number
8357  */
8358 Roo.SplitBar.LEFT = 1;
8359
8360 /**
8361  * Placement constant - The resizing element is to the right of the splitter element
8362  * @static
8363  * @type Number
8364  */
8365 Roo.SplitBar.RIGHT = 2;
8366
8367 /**
8368  * Placement constant - The resizing element is positioned above the splitter element
8369  * @static
8370  * @type Number
8371  */
8372 Roo.SplitBar.TOP = 3;
8373
8374 /**
8375  * Placement constant - The resizing element is positioned under splitter element
8376  * @static
8377  * @type Number
8378  */
8379 Roo.SplitBar.BOTTOM = 4;
8380 /*
8381  * Based on:
8382  * Ext JS Library 1.1.1
8383  * Copyright(c) 2006-2007, Ext JS, LLC.
8384  *
8385  * Originally Released Under LGPL - original licence link has changed is not relivant.
8386  *
8387  * Fork - LGPL
8388  * <script type="text/javascript">
8389  */
8390
8391 /**
8392  * @class Roo.View
8393  * @extends Roo.util.Observable
8394  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8395  * This class also supports single and multi selection modes. <br>
8396  * Create a data model bound view:
8397  <pre><code>
8398  var store = new Roo.data.Store(...);
8399
8400  var view = new Roo.View({
8401     el : "my-element",
8402     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8403  
8404     singleSelect: true,
8405     selectedClass: "ydataview-selected",
8406     store: store
8407  });
8408
8409  // listen for node click?
8410  view.on("click", function(vw, index, node, e){
8411  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8412  });
8413
8414  // load XML data
8415  dataModel.load("foobar.xml");
8416  </code></pre>
8417  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8418  * <br><br>
8419  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8420  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8421  * 
8422  * Note: old style constructor is still suported (container, template, config)
8423  * 
8424  * @constructor
8425  * Create a new View
8426  * @param {Object} config The config object
8427  * 
8428  */
8429 Roo.View = function(config, depreciated_tpl, depreciated_config){
8430     
8431     this.parent = false;
8432     
8433     if (typeof(depreciated_tpl) == 'undefined') {
8434         // new way.. - universal constructor.
8435         Roo.apply(this, config);
8436         this.el  = Roo.get(this.el);
8437     } else {
8438         // old format..
8439         this.el  = Roo.get(config);
8440         this.tpl = depreciated_tpl;
8441         Roo.apply(this, depreciated_config);
8442     }
8443     this.wrapEl  = this.el.wrap().wrap();
8444     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8445     
8446     
8447     if(typeof(this.tpl) == "string"){
8448         this.tpl = new Roo.Template(this.tpl);
8449     } else {
8450         // support xtype ctors..
8451         this.tpl = new Roo.factory(this.tpl, Roo);
8452     }
8453     
8454     
8455     this.tpl.compile();
8456     
8457     /** @private */
8458     this.addEvents({
8459         /**
8460          * @event beforeclick
8461          * Fires before a click is processed. Returns false to cancel the default action.
8462          * @param {Roo.View} this
8463          * @param {Number} index The index of the target node
8464          * @param {HTMLElement} node The target node
8465          * @param {Roo.EventObject} e The raw event object
8466          */
8467             "beforeclick" : true,
8468         /**
8469          * @event click
8470          * Fires when a template node is clicked.
8471          * @param {Roo.View} this
8472          * @param {Number} index The index of the target node
8473          * @param {HTMLElement} node The target node
8474          * @param {Roo.EventObject} e The raw event object
8475          */
8476             "click" : true,
8477         /**
8478          * @event dblclick
8479          * Fires when a template node is double clicked.
8480          * @param {Roo.View} this
8481          * @param {Number} index The index of the target node
8482          * @param {HTMLElement} node The target node
8483          * @param {Roo.EventObject} e The raw event object
8484          */
8485             "dblclick" : true,
8486         /**
8487          * @event contextmenu
8488          * Fires when a template node is right clicked.
8489          * @param {Roo.View} this
8490          * @param {Number} index The index of the target node
8491          * @param {HTMLElement} node The target node
8492          * @param {Roo.EventObject} e The raw event object
8493          */
8494             "contextmenu" : true,
8495         /**
8496          * @event selectionchange
8497          * Fires when the selected nodes change.
8498          * @param {Roo.View} this
8499          * @param {Array} selections Array of the selected nodes
8500          */
8501             "selectionchange" : true,
8502     
8503         /**
8504          * @event beforeselect
8505          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506          * @param {Roo.View} this
8507          * @param {HTMLElement} node The node to be selected
8508          * @param {Array} selections Array of currently selected nodes
8509          */
8510             "beforeselect" : true,
8511         /**
8512          * @event preparedata
8513          * Fires on every row to render, to allow you to change the data.
8514          * @param {Roo.View} this
8515          * @param {Object} data to be rendered (change this)
8516          */
8517           "preparedata" : true
8518           
8519           
8520         });
8521
8522
8523
8524     this.el.on({
8525         "click": this.onClick,
8526         "dblclick": this.onDblClick,
8527         "contextmenu": this.onContextMenu,
8528         scope:this
8529     });
8530
8531     this.selections = [];
8532     this.nodes = [];
8533     this.cmp = new Roo.CompositeElementLite([]);
8534     if(this.store){
8535         this.store = Roo.factory(this.store, Roo.data);
8536         this.setStore(this.store, true);
8537     }
8538     
8539     if ( this.footer && this.footer.xtype) {
8540            
8541          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8542         
8543         this.footer.dataSource = this.store
8544         this.footer.container = fctr;
8545         this.footer = Roo.factory(this.footer, Roo);
8546         fctr.insertFirst(this.el);
8547         
8548         // this is a bit insane - as the paging toolbar seems to detach the el..
8549 //        dom.parentNode.parentNode.parentNode
8550          // they get detached?
8551     }
8552     
8553     
8554     Roo.View.superclass.constructor.call(this);
8555     
8556     
8557 };
8558
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8560     
8561      /**
8562      * @cfg {Roo.data.Store} store Data store to load data from.
8563      */
8564     store : false,
8565     
8566     /**
8567      * @cfg {String|Roo.Element} el The container element.
8568      */
8569     el : '',
8570     
8571     /**
8572      * @cfg {String|Roo.Template} tpl The template used by this View 
8573      */
8574     tpl : false,
8575     /**
8576      * @cfg {String} dataName the named area of the template to use as the data area
8577      *                          Works with domtemplates roo-name="name"
8578      */
8579     dataName: false,
8580     /**
8581      * @cfg {String} selectedClass The css class to add to selected nodes
8582      */
8583     selectedClass : "x-view-selected",
8584      /**
8585      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8586      */
8587     emptyText : "",
8588     
8589     /**
8590      * @cfg {String} text to display on mask (default Loading)
8591      */
8592     mask : false,
8593     /**
8594      * @cfg {Boolean} multiSelect Allow multiple selection
8595      */
8596     multiSelect : false,
8597     /**
8598      * @cfg {Boolean} singleSelect Allow single selection
8599      */
8600     singleSelect:  false,
8601     
8602     /**
8603      * @cfg {Boolean} toggleSelect - selecting 
8604      */
8605     toggleSelect : false,
8606     
8607     /**
8608      * @cfg {Boolean} tickable - selecting 
8609      */
8610     tickable : false,
8611     
8612     /**
8613      * Returns the element this view is bound to.
8614      * @return {Roo.Element}
8615      */
8616     getEl : function(){
8617         return this.wrapEl;
8618     },
8619     
8620     
8621
8622     /**
8623      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8624      */
8625     refresh : function(){
8626         Roo.log('refresh');
8627         var t = this.tpl;
8628         
8629         // if we are using something like 'domtemplate', then
8630         // the what gets used is:
8631         // t.applySubtemplate(NAME, data, wrapping data..)
8632         // the outer template then get' applied with
8633         //     the store 'extra data'
8634         // and the body get's added to the
8635         //      roo-name="data" node?
8636         //      <span class='roo-tpl-{name}'></span> ?????
8637         
8638         
8639         
8640         this.clearSelections();
8641         this.el.update("");
8642         var html = [];
8643         var records = this.store.getRange();
8644         if(records.length < 1) {
8645             
8646             // is this valid??  = should it render a template??
8647             
8648             this.el.update(this.emptyText);
8649             return;
8650         }
8651         var el = this.el;
8652         if (this.dataName) {
8653             this.el.update(t.apply(this.store.meta)); //????
8654             el = this.el.child('.roo-tpl-' + this.dataName);
8655         }
8656         
8657         for(var i = 0, len = records.length; i < len; i++){
8658             var data = this.prepareData(records[i].data, i, records[i]);
8659             this.fireEvent("preparedata", this, data, i, records[i]);
8660             
8661             var d = Roo.apply({}, data);
8662             
8663             if(this.tickable){
8664                 Roo.apply(d, {'roo-id' : Roo.id()});
8665                 
8666                 var _this = this;
8667             
8668                 Roo.each(this.parent.item, function(item){
8669                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8670                         return;
8671                     }
8672                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8673                 });
8674             }
8675             
8676             html[html.length] = Roo.util.Format.trim(
8677                 this.dataName ?
8678                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8679                     t.apply(d)
8680             );
8681         }
8682         
8683         
8684         
8685         el.update(html.join(""));
8686         this.nodes = el.dom.childNodes;
8687         this.updateIndexes(0);
8688     },
8689     
8690
8691     /**
8692      * Function to override to reformat the data that is sent to
8693      * the template for each node.
8694      * DEPRICATED - use the preparedata event handler.
8695      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696      * a JSON object for an UpdateManager bound view).
8697      */
8698     prepareData : function(data, index, record)
8699     {
8700         this.fireEvent("preparedata", this, data, index, record);
8701         return data;
8702     },
8703
8704     onUpdate : function(ds, record){
8705          Roo.log('on update');   
8706         this.clearSelections();
8707         var index = this.store.indexOf(record);
8708         var n = this.nodes[index];
8709         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710         n.parentNode.removeChild(n);
8711         this.updateIndexes(index, index);
8712     },
8713
8714     
8715     
8716 // --------- FIXME     
8717     onAdd : function(ds, records, index)
8718     {
8719         Roo.log(['on Add', ds, records, index] );        
8720         this.clearSelections();
8721         if(this.nodes.length == 0){
8722             this.refresh();
8723             return;
8724         }
8725         var n = this.nodes[index];
8726         for(var i = 0, len = records.length; i < len; i++){
8727             var d = this.prepareData(records[i].data, i, records[i]);
8728             if(n){
8729                 this.tpl.insertBefore(n, d);
8730             }else{
8731                 
8732                 this.tpl.append(this.el, d);
8733             }
8734         }
8735         this.updateIndexes(index);
8736     },
8737
8738     onRemove : function(ds, record, index){
8739         Roo.log('onRemove');
8740         this.clearSelections();
8741         var el = this.dataName  ?
8742             this.el.child('.roo-tpl-' + this.dataName) :
8743             this.el; 
8744         
8745         el.dom.removeChild(this.nodes[index]);
8746         this.updateIndexes(index);
8747     },
8748
8749     /**
8750      * Refresh an individual node.
8751      * @param {Number} index
8752      */
8753     refreshNode : function(index){
8754         this.onUpdate(this.store, this.store.getAt(index));
8755     },
8756
8757     updateIndexes : function(startIndex, endIndex){
8758         var ns = this.nodes;
8759         startIndex = startIndex || 0;
8760         endIndex = endIndex || ns.length - 1;
8761         for(var i = startIndex; i <= endIndex; i++){
8762             ns[i].nodeIndex = i;
8763         }
8764     },
8765
8766     /**
8767      * Changes the data store this view uses and refresh the view.
8768      * @param {Store} store
8769      */
8770     setStore : function(store, initial){
8771         if(!initial && this.store){
8772             this.store.un("datachanged", this.refresh);
8773             this.store.un("add", this.onAdd);
8774             this.store.un("remove", this.onRemove);
8775             this.store.un("update", this.onUpdate);
8776             this.store.un("clear", this.refresh);
8777             this.store.un("beforeload", this.onBeforeLoad);
8778             this.store.un("load", this.onLoad);
8779             this.store.un("loadexception", this.onLoad);
8780         }
8781         if(store){
8782           
8783             store.on("datachanged", this.refresh, this);
8784             store.on("add", this.onAdd, this);
8785             store.on("remove", this.onRemove, this);
8786             store.on("update", this.onUpdate, this);
8787             store.on("clear", this.refresh, this);
8788             store.on("beforeload", this.onBeforeLoad, this);
8789             store.on("load", this.onLoad, this);
8790             store.on("loadexception", this.onLoad, this);
8791         }
8792         
8793         if(store){
8794             this.refresh();
8795         }
8796     },
8797     /**
8798      * onbeforeLoad - masks the loading area.
8799      *
8800      */
8801     onBeforeLoad : function(store,opts)
8802     {
8803          Roo.log('onBeforeLoad');   
8804         if (!opts.add) {
8805             this.el.update("");
8806         }
8807         this.el.mask(this.mask ? this.mask : "Loading" ); 
8808     },
8809     onLoad : function ()
8810     {
8811         this.el.unmask();
8812     },
8813     
8814
8815     /**
8816      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817      * @param {HTMLElement} node
8818      * @return {HTMLElement} The template node
8819      */
8820     findItemFromChild : function(node){
8821         var el = this.dataName  ?
8822             this.el.child('.roo-tpl-' + this.dataName,true) :
8823             this.el.dom; 
8824         
8825         if(!node || node.parentNode == el){
8826                     return node;
8827             }
8828             var p = node.parentNode;
8829             while(p && p != el){
8830             if(p.parentNode == el){
8831                 return p;
8832             }
8833             p = p.parentNode;
8834         }
8835             return null;
8836     },
8837
8838     /** @ignore */
8839     onClick : function(e){
8840         var item = this.findItemFromChild(e.getTarget());
8841         if(item){
8842             var index = this.indexOf(item);
8843             if(this.onItemClick(item, index, e) !== false){
8844                 this.fireEvent("click", this, index, item, e);
8845             }
8846         }else{
8847             this.clearSelections();
8848         }
8849     },
8850
8851     /** @ignore */
8852     onContextMenu : function(e){
8853         var item = this.findItemFromChild(e.getTarget());
8854         if(item){
8855             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8856         }
8857     },
8858
8859     /** @ignore */
8860     onDblClick : function(e){
8861         var item = this.findItemFromChild(e.getTarget());
8862         if(item){
8863             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8864         }
8865     },
8866
8867     onItemClick : function(item, index, e)
8868     {
8869         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8870             return false;
8871         }
8872         if (this.toggleSelect) {
8873             var m = this.isSelected(item) ? 'unselect' : 'select';
8874             Roo.log(m);
8875             var _t = this;
8876             _t[m](item, true, false);
8877             return true;
8878         }
8879         if(this.multiSelect || this.singleSelect){
8880             if(this.multiSelect && e.shiftKey && this.lastSelection){
8881                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8882             }else{
8883                 this.select(item, this.multiSelect && e.ctrlKey);
8884                 this.lastSelection = item;
8885             }
8886             
8887             if(!this.tickable){
8888                 e.preventDefault();
8889             }
8890             
8891         }
8892         return true;
8893     },
8894
8895     /**
8896      * Get the number of selected nodes.
8897      * @return {Number}
8898      */
8899     getSelectionCount : function(){
8900         return this.selections.length;
8901     },
8902
8903     /**
8904      * Get the currently selected nodes.
8905      * @return {Array} An array of HTMLElements
8906      */
8907     getSelectedNodes : function(){
8908         return this.selections;
8909     },
8910
8911     /**
8912      * Get the indexes of the selected nodes.
8913      * @return {Array}
8914      */
8915     getSelectedIndexes : function(){
8916         var indexes = [], s = this.selections;
8917         for(var i = 0, len = s.length; i < len; i++){
8918             indexes.push(s[i].nodeIndex);
8919         }
8920         return indexes;
8921     },
8922
8923     /**
8924      * Clear all selections
8925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8926      */
8927     clearSelections : function(suppressEvent){
8928         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929             this.cmp.elements = this.selections;
8930             this.cmp.removeClass(this.selectedClass);
8931             this.selections = [];
8932             if(!suppressEvent){
8933                 this.fireEvent("selectionchange", this, this.selections);
8934             }
8935         }
8936     },
8937
8938     /**
8939      * Returns true if the passed node is selected
8940      * @param {HTMLElement/Number} node The node or node index
8941      * @return {Boolean}
8942      */
8943     isSelected : function(node){
8944         var s = this.selections;
8945         if(s.length < 1){
8946             return false;
8947         }
8948         node = this.getNode(node);
8949         return s.indexOf(node) !== -1;
8950     },
8951
8952     /**
8953      * Selects nodes.
8954      * @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
8955      * @param {Boolean} keepExisting (optional) true to keep existing selections
8956      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8957      */
8958     select : function(nodeInfo, keepExisting, suppressEvent){
8959         if(nodeInfo instanceof Array){
8960             if(!keepExisting){
8961                 this.clearSelections(true);
8962             }
8963             for(var i = 0, len = nodeInfo.length; i < len; i++){
8964                 this.select(nodeInfo[i], true, true);
8965             }
8966             return;
8967         } 
8968         var node = this.getNode(nodeInfo);
8969         if(!node || this.isSelected(node)){
8970             return; // already selected.
8971         }
8972         if(!keepExisting){
8973             this.clearSelections(true);
8974         }
8975         
8976         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977             Roo.fly(node).addClass(this.selectedClass);
8978             this.selections.push(node);
8979             if(!suppressEvent){
8980                 this.fireEvent("selectionchange", this, this.selections);
8981             }
8982         }
8983         
8984         
8985     },
8986       /**
8987      * Unselects nodes.
8988      * @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
8989      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8991      */
8992     unselect : function(nodeInfo, keepExisting, suppressEvent)
8993     {
8994         if(nodeInfo instanceof Array){
8995             Roo.each(this.selections, function(s) {
8996                 this.unselect(s, nodeInfo);
8997             }, this);
8998             return;
8999         }
9000         var node = this.getNode(nodeInfo);
9001         if(!node || !this.isSelected(node)){
9002             Roo.log("not selected");
9003             return; // not selected.
9004         }
9005         // fireevent???
9006         var ns = [];
9007         Roo.each(this.selections, function(s) {
9008             if (s == node ) {
9009                 Roo.fly(node).removeClass(this.selectedClass);
9010
9011                 return;
9012             }
9013             ns.push(s);
9014         },this);
9015         
9016         this.selections= ns;
9017         this.fireEvent("selectionchange", this, this.selections);
9018     },
9019
9020     /**
9021      * Gets a template node.
9022      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023      * @return {HTMLElement} The node or null if it wasn't found
9024      */
9025     getNode : function(nodeInfo){
9026         if(typeof nodeInfo == "string"){
9027             return document.getElementById(nodeInfo);
9028         }else if(typeof nodeInfo == "number"){
9029             return this.nodes[nodeInfo];
9030         }
9031         return nodeInfo;
9032     },
9033
9034     /**
9035      * Gets a range template nodes.
9036      * @param {Number} startIndex
9037      * @param {Number} endIndex
9038      * @return {Array} An array of nodes
9039      */
9040     getNodes : function(start, end){
9041         var ns = this.nodes;
9042         start = start || 0;
9043         end = typeof end == "undefined" ? ns.length - 1 : end;
9044         var nodes = [];
9045         if(start <= end){
9046             for(var i = start; i <= end; i++){
9047                 nodes.push(ns[i]);
9048             }
9049         } else{
9050             for(var i = start; i >= end; i--){
9051                 nodes.push(ns[i]);
9052             }
9053         }
9054         return nodes;
9055     },
9056
9057     /**
9058      * Finds the index of the passed node
9059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060      * @return {Number} The index of the node or -1
9061      */
9062     indexOf : function(node){
9063         node = this.getNode(node);
9064         if(typeof node.nodeIndex == "number"){
9065             return node.nodeIndex;
9066         }
9067         var ns = this.nodes;
9068         for(var i = 0, len = ns.length; i < len; i++){
9069             if(ns[i] == node){
9070                 return i;
9071             }
9072         }
9073         return -1;
9074     }
9075 });
9076 /*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088  * @class Roo.JsonView
9089  * @extends Roo.View
9090  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9091 <pre><code>
9092 var view = new Roo.JsonView({
9093     container: "my-element",
9094     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9095     multiSelect: true, 
9096     jsonRoot: "data" 
9097 });
9098
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102 });
9103
9104 // direct load of JSON data
9105 view.load("foobar.php");
9106
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109     '&lt;div class="entry"&gt;' +
9110     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9111     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9112     "&lt;/div&gt;&lt;hr /&gt;"
9113 );
9114
9115 var moreView = new Roo.JsonView({
9116     container :  "entry-list", 
9117     template : tpl,
9118     jsonRoot: "posts"
9119 });
9120 moreView.on("beforerender", this.sortEntries, this);
9121 moreView.load({
9122     url: "/blog/get-posts.php",
9123     params: "allposts=true",
9124     text: "Loading Blog Entries..."
9125 });
9126 </code></pre>
9127
9128 * Note: old code is supported with arguments : (container, template, config)
9129
9130
9131  * @constructor
9132  * Create a new JsonView
9133  * 
9134  * @param {Object} config The config object
9135  * 
9136  */
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9138     
9139     
9140     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9141
9142     var um = this.el.getUpdateManager();
9143     um.setRenderer(this);
9144     um.on("update", this.onLoad, this);
9145     um.on("failure", this.onLoadException, this);
9146
9147     /**
9148      * @event beforerender
9149      * Fires before rendering of the downloaded JSON data.
9150      * @param {Roo.JsonView} this
9151      * @param {Object} data The JSON data loaded
9152      */
9153     /**
9154      * @event load
9155      * Fires when data is loaded.
9156      * @param {Roo.JsonView} this
9157      * @param {Object} data The JSON data loaded
9158      * @param {Object} response The raw Connect response object
9159      */
9160     /**
9161      * @event loadexception
9162      * Fires when loading fails.
9163      * @param {Roo.JsonView} this
9164      * @param {Object} response The raw Connect response object
9165      */
9166     this.addEvents({
9167         'beforerender' : true,
9168         'load' : true,
9169         'loadexception' : true
9170     });
9171 };
9172 Roo.extend(Roo.JsonView, Roo.View, {
9173     /**
9174      * @type {String} The root property in the loaded JSON object that contains the data
9175      */
9176     jsonRoot : "",
9177
9178     /**
9179      * Refreshes the view.
9180      */
9181     refresh : function(){
9182         this.clearSelections();
9183         this.el.update("");
9184         var html = [];
9185         var o = this.jsonData;
9186         if(o && o.length > 0){
9187             for(var i = 0, len = o.length; i < len; i++){
9188                 var data = this.prepareData(o[i], i, o);
9189                 html[html.length] = this.tpl.apply(data);
9190             }
9191         }else{
9192             html.push(this.emptyText);
9193         }
9194         this.el.update(html.join(""));
9195         this.nodes = this.el.dom.childNodes;
9196         this.updateIndexes(0);
9197     },
9198
9199     /**
9200      * 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.
9201      * @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:
9202      <pre><code>
9203      view.load({
9204          url: "your-url.php",
9205          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206          callback: yourFunction,
9207          scope: yourObject, //(optional scope)
9208          discardUrl: false,
9209          nocache: false,
9210          text: "Loading...",
9211          timeout: 30,
9212          scripts: false
9213      });
9214      </code></pre>
9215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216      * 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.
9217      * @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}
9218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219      * @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.
9220      */
9221     load : function(){
9222         var um = this.el.getUpdateManager();
9223         um.update.apply(um, arguments);
9224     },
9225
9226     render : function(el, response){
9227         this.clearSelections();
9228         this.el.update("");
9229         var o;
9230         try{
9231             o = Roo.util.JSON.decode(response.responseText);
9232             if(this.jsonRoot){
9233                 
9234                 o = o[this.jsonRoot];
9235             }
9236         } catch(e){
9237         }
9238         /**
9239          * The current JSON data or null
9240          */
9241         this.jsonData = o;
9242         this.beforeRender();
9243         this.refresh();
9244     },
9245
9246 /**
9247  * Get the number of records in the current JSON dataset
9248  * @return {Number}
9249  */
9250     getCount : function(){
9251         return this.jsonData ? this.jsonData.length : 0;
9252     },
9253
9254 /**
9255  * Returns the JSON object for the specified node(s)
9256  * @param {HTMLElement/Array} node The node or an array of nodes
9257  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258  * you get the JSON object for the node
9259  */
9260     getNodeData : function(node){
9261         if(node instanceof Array){
9262             var data = [];
9263             for(var i = 0, len = node.length; i < len; i++){
9264                 data.push(this.getNodeData(node[i]));
9265             }
9266             return data;
9267         }
9268         return this.jsonData[this.indexOf(node)] || null;
9269     },
9270
9271     beforeRender : function(){
9272         this.snapshot = this.jsonData;
9273         if(this.sortInfo){
9274             this.sort.apply(this, this.sortInfo);
9275         }
9276         this.fireEvent("beforerender", this, this.jsonData);
9277     },
9278
9279     onLoad : function(el, o){
9280         this.fireEvent("load", this, this.jsonData, o);
9281     },
9282
9283     onLoadException : function(el, o){
9284         this.fireEvent("loadexception", this, o);
9285     },
9286
9287 /**
9288  * Filter the data by a specific property.
9289  * @param {String} property A property on your JSON objects
9290  * @param {String/RegExp} value Either string that the property values
9291  * should start with, or a RegExp to test against the property
9292  */
9293     filter : function(property, value){
9294         if(this.jsonData){
9295             var data = [];
9296             var ss = this.snapshot;
9297             if(typeof value == "string"){
9298                 var vlen = value.length;
9299                 if(vlen == 0){
9300                     this.clearFilter();
9301                     return;
9302                 }
9303                 value = value.toLowerCase();
9304                 for(var i = 0, len = ss.length; i < len; i++){
9305                     var o = ss[i];
9306                     if(o[property].substr(0, vlen).toLowerCase() == value){
9307                         data.push(o);
9308                     }
9309                 }
9310             } else if(value.exec){ // regex?
9311                 for(var i = 0, len = ss.length; i < len; i++){
9312                     var o = ss[i];
9313                     if(value.test(o[property])){
9314                         data.push(o);
9315                     }
9316                 }
9317             } else{
9318                 return;
9319             }
9320             this.jsonData = data;
9321             this.refresh();
9322         }
9323     },
9324
9325 /**
9326  * Filter by a function. The passed function will be called with each
9327  * object in the current dataset. If the function returns true the value is kept,
9328  * otherwise it is filtered.
9329  * @param {Function} fn
9330  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9331  */
9332     filterBy : function(fn, scope){
9333         if(this.jsonData){
9334             var data = [];
9335             var ss = this.snapshot;
9336             for(var i = 0, len = ss.length; i < len; i++){
9337                 var o = ss[i];
9338                 if(fn.call(scope || this, o)){
9339                     data.push(o);
9340                 }
9341             }
9342             this.jsonData = data;
9343             this.refresh();
9344         }
9345     },
9346
9347 /**
9348  * Clears the current filter.
9349  */
9350     clearFilter : function(){
9351         if(this.snapshot && this.jsonData != this.snapshot){
9352             this.jsonData = this.snapshot;
9353             this.refresh();
9354         }
9355     },
9356
9357
9358 /**
9359  * Sorts the data for this view and refreshes it.
9360  * @param {String} property A property on your JSON objects to sort on
9361  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9363  */
9364     sort : function(property, dir, sortType){
9365         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9366         if(this.jsonData){
9367             var p = property;
9368             var dsc = dir && dir.toLowerCase() == "desc";
9369             var f = function(o1, o2){
9370                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9372                 ;
9373                 if(v1 < v2){
9374                     return dsc ? +1 : -1;
9375                 } else if(v1 > v2){
9376                     return dsc ? -1 : +1;
9377                 } else{
9378                     return 0;
9379                 }
9380             };
9381             this.jsonData.sort(f);
9382             this.refresh();
9383             if(this.jsonData != this.snapshot){
9384                 this.snapshot.sort(f);
9385             }
9386         }
9387     }
9388 });/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398  
9399
9400 /**
9401  * @class Roo.ColorPalette
9402  * @extends Roo.Component
9403  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9404  * Here's an example of typical usage:
9405  * <pre><code>
9406 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9407 cp.render('my-div');
9408
9409 cp.on('select', function(palette, selColor){
9410     // do something with selColor
9411 });
9412 </code></pre>
9413  * @constructor
9414  * Create a new ColorPalette
9415  * @param {Object} config The config object
9416  */
9417 Roo.ColorPalette = function(config){
9418     Roo.ColorPalette.superclass.constructor.call(this, config);
9419     this.addEvents({
9420         /**
9421              * @event select
9422              * Fires when a color is selected
9423              * @param {ColorPalette} this
9424              * @param {String} color The 6-digit color hex code (without the # symbol)
9425              */
9426         select: true
9427     });
9428
9429     if(this.handler){
9430         this.on("select", this.handler, this.scope, true);
9431     }
9432 };
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9434     /**
9435      * @cfg {String} itemCls
9436      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9437      */
9438     itemCls : "x-color-palette",
9439     /**
9440      * @cfg {String} value
9441      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9442      * the hex codes are case-sensitive.
9443      */
9444     value : null,
9445     clickEvent:'click',
9446     // private
9447     ctype: "Roo.ColorPalette",
9448
9449     /**
9450      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9451      */
9452     allowReselect : false,
9453
9454     /**
9455      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9456      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9457      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458      * of colors with the width setting until the box is symmetrical.</p>
9459      * <p>You can override individual colors if needed:</p>
9460      * <pre><code>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000";  // change the first box to red
9463 </code></pre>
9464
9465 Or you can provide a custom array of your own for complete control:
9466 <pre><code>
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9469 </code></pre>
9470      * @type Array
9471      */
9472     colors : [
9473         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9478     ],
9479
9480     // private
9481     onRender : function(container, position){
9482         var t = new Roo.MasterTemplate(
9483             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9484         );
9485         var c = this.colors;
9486         for(var i = 0, len = c.length; i < len; i++){
9487             t.add([c[i]]);
9488         }
9489         var el = document.createElement("div");
9490         el.className = this.itemCls;
9491         t.overwrite(el);
9492         container.dom.insertBefore(el, position);
9493         this.el = Roo.get(el);
9494         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9495         if(this.clickEvent != 'click'){
9496             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9497         }
9498     },
9499
9500     // private
9501     afterRender : function(){
9502         Roo.ColorPalette.superclass.afterRender.call(this);
9503         if(this.value){
9504             var s = this.value;
9505             this.value = null;
9506             this.select(s);
9507         }
9508     },
9509
9510     // private
9511     handleClick : function(e, t){
9512         e.preventDefault();
9513         if(!this.disabled){
9514             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515             this.select(c.toUpperCase());
9516         }
9517     },
9518
9519     /**
9520      * Selects the specified color in the palette (fires the select event)
9521      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9522      */
9523     select : function(color){
9524         color = color.replace("#", "");
9525         if(color != this.value || this.allowReselect){
9526             var el = this.el;
9527             if(this.value){
9528                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9529             }
9530             el.child("a.color-"+color).addClass("x-color-palette-sel");
9531             this.value = color;
9532             this.fireEvent("select", this, color);
9533         }
9534     }
9535 });/*
9536  * Based on:
9537  * Ext JS Library 1.1.1
9538  * Copyright(c) 2006-2007, Ext JS, LLC.
9539  *
9540  * Originally Released Under LGPL - original licence link has changed is not relivant.
9541  *
9542  * Fork - LGPL
9543  * <script type="text/javascript">
9544  */
9545  
9546 /**
9547  * @class Roo.DatePicker
9548  * @extends Roo.Component
9549  * Simple date picker class.
9550  * @constructor
9551  * Create a new DatePicker
9552  * @param {Object} config The config object
9553  */
9554 Roo.DatePicker = function(config){
9555     Roo.DatePicker.superclass.constructor.call(this, config);
9556
9557     this.value = config && config.value ?
9558                  config.value.clearTime() : new Date().clearTime();
9559
9560     this.addEvents({
9561         /**
9562              * @event select
9563              * Fires when a date is selected
9564              * @param {DatePicker} this
9565              * @param {Date} date The selected date
9566              */
9567         'select': true,
9568         /**
9569              * @event monthchange
9570              * Fires when the displayed month changes 
9571              * @param {DatePicker} this
9572              * @param {Date} date The selected month
9573              */
9574         'monthchange': true
9575     });
9576
9577     if(this.handler){
9578         this.on("select", this.handler,  this.scope || this);
9579     }
9580     // build the disabledDatesRE
9581     if(!this.disabledDatesRE && this.disabledDates){
9582         var dd = this.disabledDates;
9583         var re = "(?:";
9584         for(var i = 0; i < dd.length; i++){
9585             re += dd[i];
9586             if(i != dd.length-1) re += "|";
9587         }
9588         this.disabledDatesRE = new RegExp(re + ")");
9589     }
9590 };
9591
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9593     /**
9594      * @cfg {String} todayText
9595      * The text to display on the button that selects the current date (defaults to "Today")
9596      */
9597     todayText : "Today",
9598     /**
9599      * @cfg {String} okText
9600      * The text to display on the ok button
9601      */
9602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9603     /**
9604      * @cfg {String} cancelText
9605      * The text to display on the cancel button
9606      */
9607     cancelText : "Cancel",
9608     /**
9609      * @cfg {String} todayTip
9610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9611      */
9612     todayTip : "{0} (Spacebar)",
9613     /**
9614      * @cfg {Date} minDate
9615      * Minimum allowable date (JavaScript date object, defaults to null)
9616      */
9617     minDate : null,
9618     /**
9619      * @cfg {Date} maxDate
9620      * Maximum allowable date (JavaScript date object, defaults to null)
9621      */
9622     maxDate : null,
9623     /**
9624      * @cfg {String} minText
9625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9626      */
9627     minText : "This date is before the minimum date",
9628     /**
9629      * @cfg {String} maxText
9630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9631      */
9632     maxText : "This date is after the maximum date",
9633     /**
9634      * @cfg {String} format
9635      * The default date format string which can be overriden for localization support.  The format must be
9636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9637      */
9638     format : "m/d/y",
9639     /**
9640      * @cfg {Array} disabledDays
9641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9642      */
9643     disabledDays : null,
9644     /**
9645      * @cfg {String} disabledDaysText
9646      * The tooltip to display when the date falls on a disabled day (defaults to "")
9647      */
9648     disabledDaysText : "",
9649     /**
9650      * @cfg {RegExp} disabledDatesRE
9651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9652      */
9653     disabledDatesRE : null,
9654     /**
9655      * @cfg {String} disabledDatesText
9656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9657      */
9658     disabledDatesText : "",
9659     /**
9660      * @cfg {Boolean} constrainToViewport
9661      * True to constrain the date picker to the viewport (defaults to true)
9662      */
9663     constrainToViewport : true,
9664     /**
9665      * @cfg {Array} monthNames
9666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9667      */
9668     monthNames : Date.monthNames,
9669     /**
9670      * @cfg {Array} dayNames
9671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9672      */
9673     dayNames : Date.dayNames,
9674     /**
9675      * @cfg {String} nextText
9676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9677      */
9678     nextText: 'Next Month (Control+Right)',
9679     /**
9680      * @cfg {String} prevText
9681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9682      */
9683     prevText: 'Previous Month (Control+Left)',
9684     /**
9685      * @cfg {String} monthYearText
9686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9687      */
9688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9689     /**
9690      * @cfg {Number} startDay
9691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9692      */
9693     startDay : 0,
9694     /**
9695      * @cfg {Bool} showClear
9696      * Show a clear button (usefull for date form elements that can be blank.)
9697      */
9698     
9699     showClear: false,
9700     
9701     /**
9702      * Sets the value of the date field
9703      * @param {Date} value The date to set
9704      */
9705     setValue : function(value){
9706         var old = this.value;
9707         
9708         if (typeof(value) == 'string') {
9709          
9710             value = Date.parseDate(value, this.format);
9711         }
9712         if (!value) {
9713             value = new Date();
9714         }
9715         
9716         this.value = value.clearTime(true);
9717         if(this.el){
9718             this.update(this.value);
9719         }
9720     },
9721
9722     /**
9723      * Gets the current selected value of the date field
9724      * @return {Date} The selected date
9725      */
9726     getValue : function(){
9727         return this.value;
9728     },
9729
9730     // private
9731     focus : function(){
9732         if(this.el){
9733             this.update(this.activeDate);
9734         }
9735     },
9736
9737     // privateval
9738     onRender : function(container, position){
9739         
9740         var m = [
9741              '<table cellspacing="0">',
9742                 '<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>',
9743                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744         var dn = this.dayNames;
9745         for(var i = 0; i < 7; i++){
9746             var d = this.startDay+i;
9747             if(d > 6){
9748                 d = d-7;
9749             }
9750             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9751         }
9752         m[m.length] = "</tr></thead><tbody><tr>";
9753         for(var i = 0; i < 42; i++) {
9754             if(i % 7 == 0 && i != 0){
9755                 m[m.length] = "</tr><tr>";
9756             }
9757             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9758         }
9759         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9761
9762         var el = document.createElement("div");
9763         el.className = "x-date-picker";
9764         el.innerHTML = m.join("");
9765
9766         container.dom.insertBefore(el, position);
9767
9768         this.el = Roo.get(el);
9769         this.eventEl = Roo.get(el.firstChild);
9770
9771         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772             handler: this.showPrevMonth,
9773             scope: this,
9774             preventDefault:true,
9775             stopDefault:true
9776         });
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779             handler: this.showNextMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9786
9787         this.monthPicker = this.el.down('div.x-date-mp');
9788         this.monthPicker.enableDisplayMode('block');
9789         
9790         var kn = new Roo.KeyNav(this.eventEl, {
9791             "left" : function(e){
9792                 e.ctrlKey ?
9793                     this.showPrevMonth() :
9794                     this.update(this.activeDate.add("d", -1));
9795             },
9796
9797             "right" : function(e){
9798                 e.ctrlKey ?
9799                     this.showNextMonth() :
9800                     this.update(this.activeDate.add("d", 1));
9801             },
9802
9803             "up" : function(e){
9804                 e.ctrlKey ?
9805                     this.showNextYear() :
9806                     this.update(this.activeDate.add("d", -7));
9807             },
9808
9809             "down" : function(e){
9810                 e.ctrlKey ?
9811                     this.showPrevYear() :
9812                     this.update(this.activeDate.add("d", 7));
9813             },
9814
9815             "pageUp" : function(e){
9816                 this.showNextMonth();
9817             },
9818
9819             "pageDown" : function(e){
9820                 this.showPrevMonth();
9821             },
9822
9823             "enter" : function(e){
9824                 e.stopPropagation();
9825                 return true;
9826             },
9827
9828             scope : this
9829         });
9830
9831         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9832
9833         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9834
9835         this.el.unselectable();
9836         
9837         this.cells = this.el.select("table.x-date-inner tbody td");
9838         this.textNodes = this.el.query("table.x-date-inner tbody span");
9839
9840         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9841             text: "&#160;",
9842             tooltip: this.monthYearText
9843         });
9844
9845         this.mbtn.on('click', this.showMonthPicker, this);
9846         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9847
9848
9849         var today = (new Date()).dateFormat(this.format);
9850         
9851         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852         if (this.showClear) {
9853             baseTb.add( new Roo.Toolbar.Fill());
9854         }
9855         baseTb.add({
9856             text: String.format(this.todayText, today),
9857             tooltip: String.format(this.todayTip, today),
9858             handler: this.selectToday,
9859             scope: this
9860         });
9861         
9862         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9863             
9864         //});
9865         if (this.showClear) {
9866             
9867             baseTb.add( new Roo.Toolbar.Fill());
9868             baseTb.add({
9869                 text: '&#160;',
9870                 cls: 'x-btn-icon x-btn-clear',
9871                 handler: function() {
9872                     //this.value = '';
9873                     this.fireEvent("select", this, '');
9874                 },
9875                 scope: this
9876             });
9877         }
9878         
9879         
9880         if(Roo.isIE){
9881             this.el.repaint();
9882         }
9883         this.update(this.value);
9884     },
9885
9886     createMonthPicker : function(){
9887         if(!this.monthPicker.dom.firstChild){
9888             var buf = ['<table border="0" cellspacing="0">'];
9889             for(var i = 0; i < 6; i++){
9890                 buf.push(
9891                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9893                     i == 0 ?
9894                     '<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>' :
9895                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9896                 );
9897             }
9898             buf.push(
9899                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9900                     this.okText,
9901                     '</button><button type="button" class="x-date-mp-cancel">',
9902                     this.cancelText,
9903                     '</button></td></tr>',
9904                 '</table>'
9905             );
9906             this.monthPicker.update(buf.join(''));
9907             this.monthPicker.on('click', this.onMonthClick, this);
9908             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9909
9910             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9912
9913             this.mpMonths.each(function(m, a, i){
9914                 i += 1;
9915                 if((i%2) == 0){
9916                     m.dom.xmonth = 5 + Math.round(i * .5);
9917                 }else{
9918                     m.dom.xmonth = Math.round((i-1) * .5);
9919                 }
9920             });
9921         }
9922     },
9923
9924     showMonthPicker : function(){
9925         this.createMonthPicker();
9926         var size = this.el.getSize();
9927         this.monthPicker.setSize(size);
9928         this.monthPicker.child('table').setSize(size);
9929
9930         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931         this.updateMPMonth(this.mpSelMonth);
9932         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933         this.updateMPYear(this.mpSelYear);
9934
9935         this.monthPicker.slideIn('t', {duration:.2});
9936     },
9937
9938     updateMPYear : function(y){
9939         this.mpyear = y;
9940         var ys = this.mpYears.elements;
9941         for(var i = 1; i <= 10; i++){
9942             var td = ys[i-1], y2;
9943             if((i%2) == 0){
9944                 y2 = y + Math.round(i * .5);
9945                 td.firstChild.innerHTML = y2;
9946                 td.xyear = y2;
9947             }else{
9948                 y2 = y - (5-Math.round(i * .5));
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }
9952             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9953         }
9954     },
9955
9956     updateMPMonth : function(sm){
9957         this.mpMonths.each(function(m, a, i){
9958             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9959         });
9960     },
9961
9962     selectMPMonth: function(m){
9963         
9964     },
9965
9966     onMonthClick : function(e, t){
9967         e.stopEvent();
9968         var el = new Roo.Element(t), pn;
9969         if(el.is('button.x-date-mp-cancel')){
9970             this.hideMonthPicker();
9971         }
9972         else if(el.is('button.x-date-mp-ok')){
9973             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-month', 2)){
9977             this.mpMonths.removeClass('x-date-mp-sel');
9978             pn.addClass('x-date-mp-sel');
9979             this.mpSelMonth = pn.dom.xmonth;
9980         }
9981         else if(pn = el.up('td.x-date-mp-year', 2)){
9982             this.mpYears.removeClass('x-date-mp-sel');
9983             pn.addClass('x-date-mp-sel');
9984             this.mpSelYear = pn.dom.xyear;
9985         }
9986         else if(el.is('a.x-date-mp-prev')){
9987             this.updateMPYear(this.mpyear-10);
9988         }
9989         else if(el.is('a.x-date-mp-next')){
9990             this.updateMPYear(this.mpyear+10);
9991         }
9992     },
9993
9994     onMonthDblClick : function(e, t){
9995         e.stopEvent();
9996         var el = new Roo.Element(t), pn;
9997         if(pn = el.up('td.x-date-mp-month', 2)){
9998             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999             this.hideMonthPicker();
10000         }
10001         else if(pn = el.up('td.x-date-mp-year', 2)){
10002             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005     },
10006
10007     hideMonthPicker : function(disableAnim){
10008         if(this.monthPicker){
10009             if(disableAnim === true){
10010                 this.monthPicker.hide();
10011             }else{
10012                 this.monthPicker.slideOut('t', {duration:.2});
10013             }
10014         }
10015     },
10016
10017     // private
10018     showPrevMonth : function(e){
10019         this.update(this.activeDate.add("mo", -1));
10020     },
10021
10022     // private
10023     showNextMonth : function(e){
10024         this.update(this.activeDate.add("mo", 1));
10025     },
10026
10027     // private
10028     showPrevYear : function(){
10029         this.update(this.activeDate.add("y", -1));
10030     },
10031
10032     // private
10033     showNextYear : function(){
10034         this.update(this.activeDate.add("y", 1));
10035     },
10036
10037     // private
10038     handleMouseWheel : function(e){
10039         var delta = e.getWheelDelta();
10040         if(delta > 0){
10041             this.showPrevMonth();
10042             e.stopEvent();
10043         } else if(delta < 0){
10044             this.showNextMonth();
10045             e.stopEvent();
10046         }
10047     },
10048
10049     // private
10050     handleDateClick : function(e, t){
10051         e.stopEvent();
10052         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053             this.setValue(new Date(t.dateValue));
10054             this.fireEvent("select", this, this.value);
10055         }
10056     },
10057
10058     // private
10059     selectToday : function(){
10060         this.setValue(new Date().clearTime());
10061         this.fireEvent("select", this, this.value);
10062     },
10063
10064     // private
10065     update : function(date)
10066     {
10067         var vd = this.activeDate;
10068         this.activeDate = date;
10069         if(vd && this.el){
10070             var t = date.getTime();
10071             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072                 this.cells.removeClass("x-date-selected");
10073                 this.cells.each(function(c){
10074                    if(c.dom.firstChild.dateValue == t){
10075                        c.addClass("x-date-selected");
10076                        setTimeout(function(){
10077                             try{c.dom.firstChild.focus();}catch(e){}
10078                        }, 50);
10079                        return false;
10080                    }
10081                 });
10082                 return;
10083             }
10084         }
10085         
10086         var days = date.getDaysInMonth();
10087         var firstOfMonth = date.getFirstDateOfMonth();
10088         var startingPos = firstOfMonth.getDay()-this.startDay;
10089
10090         if(startingPos <= this.startDay){
10091             startingPos += 7;
10092         }
10093
10094         var pm = date.add("mo", -1);
10095         var prevStart = pm.getDaysInMonth()-startingPos;
10096
10097         var cells = this.cells.elements;
10098         var textEls = this.textNodes;
10099         days += startingPos;
10100
10101         // convert everything to numbers so it's fast
10102         var day = 86400000;
10103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104         var today = new Date().clearTime().getTime();
10105         var sel = date.clearTime().getTime();
10106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108         var ddMatch = this.disabledDatesRE;
10109         var ddText = this.disabledDatesText;
10110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111         var ddaysText = this.disabledDaysText;
10112         var format = this.format;
10113
10114         var setCellClass = function(cal, cell){
10115             cell.title = "";
10116             var t = d.getTime();
10117             cell.firstChild.dateValue = t;
10118             if(t == today){
10119                 cell.className += " x-date-today";
10120                 cell.title = cal.todayText;
10121             }
10122             if(t == sel){
10123                 cell.className += " x-date-selected";
10124                 setTimeout(function(){
10125                     try{cell.firstChild.focus();}catch(e){}
10126                 }, 50);
10127             }
10128             // disabling
10129             if(t < min) {
10130                 cell.className = " x-date-disabled";
10131                 cell.title = cal.minText;
10132                 return;
10133             }
10134             if(t > max) {
10135                 cell.className = " x-date-disabled";
10136                 cell.title = cal.maxText;
10137                 return;
10138             }
10139             if(ddays){
10140                 if(ddays.indexOf(d.getDay()) != -1){
10141                     cell.title = ddaysText;
10142                     cell.className = " x-date-disabled";
10143                 }
10144             }
10145             if(ddMatch && format){
10146                 var fvalue = d.dateFormat(format);
10147                 if(ddMatch.test(fvalue)){
10148                     cell.title = ddText.replace("%0", fvalue);
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152         };
10153
10154         var i = 0;
10155         for(; i < startingPos; i++) {
10156             textEls[i].innerHTML = (++prevStart);
10157             d.setDate(d.getDate()+1);
10158             cells[i].className = "x-date-prevday";
10159             setCellClass(this, cells[i]);
10160         }
10161         for(; i < days; i++){
10162             intDay = i - startingPos + 1;
10163             textEls[i].innerHTML = (intDay);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-active";
10166             setCellClass(this, cells[i]);
10167         }
10168         var extraDays = 0;
10169         for(; i < 42; i++) {
10170              textEls[i].innerHTML = (++extraDays);
10171              d.setDate(d.getDate()+1);
10172              cells[i].className = "x-date-nextday";
10173              setCellClass(this, cells[i]);
10174         }
10175
10176         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177         this.fireEvent('monthchange', this, date);
10178         
10179         if(!this.internalRender){
10180             var main = this.el.dom.firstChild;
10181             var w = main.offsetWidth;
10182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183             Roo.fly(main).setWidth(w);
10184             this.internalRender = true;
10185             // opera does not respect the auto grow header center column
10186             // then, after it gets a width opera refuses to recalculate
10187             // without a second pass
10188             if(Roo.isOpera && !this.secondPass){
10189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190                 this.secondPass = true;
10191                 this.update.defer(10, this, [date]);
10192             }
10193         }
10194         
10195         
10196     }
10197 });        /*
10198  * Based on:
10199  * Ext JS Library 1.1.1
10200  * Copyright(c) 2006-2007, Ext JS, LLC.
10201  *
10202  * Originally Released Under LGPL - original licence link has changed is not relivant.
10203  *
10204  * Fork - LGPL
10205  * <script type="text/javascript">
10206  */
10207 /**
10208  * @class Roo.TabPanel
10209  * @extends Roo.util.Observable
10210  * A lightweight tab container.
10211  * <br><br>
10212  * Usage:
10213  * <pre><code>
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10219
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10223
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10229
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10233
10234 // Disabled tab
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10236 tab4.disable();
10237
10238 jtabs.activate("jtabs-1");
10239  * </code></pre>
10240  * @constructor
10241  * Create a new TabPanel.
10242  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10244  */
10245 Roo.TabPanel = function(container, config){
10246     /**
10247     * The container element for this TabPanel.
10248     * @type Roo.Element
10249     */
10250     this.el = Roo.get(container, true);
10251     if(config){
10252         if(typeof config == "boolean"){
10253             this.tabPosition = config ? "bottom" : "top";
10254         }else{
10255             Roo.apply(this, config);
10256         }
10257     }
10258     if(this.tabPosition == "bottom"){
10259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260         this.el.addClass("x-tabs-bottom");
10261     }
10262     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10265     if(Roo.isIE){
10266         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10267     }
10268     if(this.tabPosition != "bottom"){
10269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270          * @type Roo.Element
10271          */
10272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273         this.el.addClass("x-tabs-top");
10274     }
10275     this.items = [];
10276
10277     this.bodyEl.setStyle("position", "relative");
10278
10279     this.active = null;
10280     this.activateDelegate = this.activate.createDelegate(this);
10281
10282     this.addEvents({
10283         /**
10284          * @event tabchange
10285          * Fires when the active tab changes
10286          * @param {Roo.TabPanel} this
10287          * @param {Roo.TabPanelItem} activePanel The new active tab
10288          */
10289         "tabchange": true,
10290         /**
10291          * @event beforetabchange
10292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293          * @param {Roo.TabPanel} this
10294          * @param {Object} e Set cancel to true on this object to cancel the tab change
10295          * @param {Roo.TabPanelItem} tab The tab being changed to
10296          */
10297         "beforetabchange" : true
10298     });
10299
10300     Roo.EventManager.onWindowResize(this.onResize, this);
10301     this.cpad = this.el.getPadding("lr");
10302     this.hiddenCount = 0;
10303
10304
10305     // toolbar on the tabbar support...
10306     if (this.toolbar) {
10307         var tcfg = this.toolbar;
10308         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10309         this.toolbar = new Roo.Toolbar(tcfg);
10310         if (Roo.isSafari) {
10311             var tbl = tcfg.container.child('table', true);
10312             tbl.setAttribute('width', '100%');
10313         }
10314         
10315     }
10316    
10317
10318
10319     Roo.TabPanel.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10323     /*
10324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10325      */
10326     tabPosition : "top",
10327     /*
10328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10329      */
10330     currentTabWidth : 0,
10331     /*
10332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10333      */
10334     minTabWidth : 40,
10335     /*
10336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     maxTabWidth : 250,
10339     /*
10340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     preferredTabWidth : 175,
10343     /*
10344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10345      */
10346     resizeTabs : false,
10347     /*
10348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10349      */
10350     monitorResize : true,
10351     /*
10352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10353      */
10354     toolbar : false,
10355
10356     /**
10357      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358      * @param {String} id The id of the div to use <b>or create</b>
10359      * @param {String} text The text for the tab
10360      * @param {String} content (optional) Content to put in the TabPanelItem body
10361      * @param {Boolean} closable (optional) True to create a close icon on the tab
10362      * @return {Roo.TabPanelItem} The created TabPanelItem
10363      */
10364     addTab : function(id, text, content, closable){
10365         var item = new Roo.TabPanelItem(this, id, text, closable);
10366         this.addTabItem(item);
10367         if(content){
10368             item.setContent(content);
10369         }
10370         return item;
10371     },
10372
10373     /**
10374      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376      * @return {Roo.TabPanelItem}
10377      */
10378     getTab : function(id){
10379         return this.items[id];
10380     },
10381
10382     /**
10383      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10385      */
10386     hideTab : function(id){
10387         var t = this.items[id];
10388         if(!t.isHidden()){
10389            t.setHidden(true);
10390            this.hiddenCount++;
10391            this.autoSizeTabs();
10392         }
10393     },
10394
10395     /**
10396      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10398      */
10399     unhideTab : function(id){
10400         var t = this.items[id];
10401         if(t.isHidden()){
10402            t.setHidden(false);
10403            this.hiddenCount--;
10404            this.autoSizeTabs();
10405         }
10406     },
10407
10408     /**
10409      * Adds an existing {@link Roo.TabPanelItem}.
10410      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10411      */
10412     addTabItem : function(item){
10413         this.items[item.id] = item;
10414         this.items.push(item);
10415         if(this.resizeTabs){
10416            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417            this.autoSizeTabs();
10418         }else{
10419             item.autoSize();
10420         }
10421     },
10422
10423     /**
10424      * Removes a {@link Roo.TabPanelItem}.
10425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10426      */
10427     removeTab : function(id){
10428         var items = this.items;
10429         var tab = items[id];
10430         if(!tab) { return; }
10431         var index = items.indexOf(tab);
10432         if(this.active == tab && items.length > 1){
10433             var newTab = this.getNextAvailable(index);
10434             if(newTab) {
10435                 newTab.activate();
10436             }
10437         }
10438         this.stripEl.dom.removeChild(tab.pnode.dom);
10439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10441         }
10442         items.splice(index, 1);
10443         delete this.items[tab.id];
10444         tab.fireEvent("close", tab);
10445         tab.purgeListeners();
10446         this.autoSizeTabs();
10447     },
10448
10449     getNextAvailable : function(start){
10450         var items = this.items;
10451         var index = start;
10452         // look for a next tab that will slide over to
10453         // replace the one being removed
10454         while(index < items.length){
10455             var item = items[++index];
10456             if(item && !item.isHidden()){
10457                 return item;
10458             }
10459         }
10460         // if one isn't found select the previous tab (on the left)
10461         index = start;
10462         while(index >= 0){
10463             var item = items[--index];
10464             if(item && !item.isHidden()){
10465                 return item;
10466             }
10467         }
10468         return null;
10469     },
10470
10471     /**
10472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10474      */
10475     disableTab : function(id){
10476         var tab = this.items[id];
10477         if(tab && this.active != tab){
10478             tab.disable();
10479         }
10480     },
10481
10482     /**
10483      * Enables a {@link Roo.TabPanelItem} that is disabled.
10484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10485      */
10486     enableTab : function(id){
10487         var tab = this.items[id];
10488         tab.enable();
10489     },
10490
10491     /**
10492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494      * @return {Roo.TabPanelItem} The TabPanelItem.
10495      */
10496     activate : function(id){
10497         var tab = this.items[id];
10498         if(!tab){
10499             return null;
10500         }
10501         if(tab == this.active || tab.disabled){
10502             return tab;
10503         }
10504         var e = {};
10505         this.fireEvent("beforetabchange", this, e, tab);
10506         if(e.cancel !== true && !tab.disabled){
10507             if(this.active){
10508                 this.active.hide();
10509             }
10510             this.active = this.items[id];
10511             this.active.show();
10512             this.fireEvent("tabchange", this, this.active);
10513         }
10514         return tab;
10515     },
10516
10517     /**
10518      * Gets the active {@link Roo.TabPanelItem}.
10519      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10520      */
10521     getActiveTab : function(){
10522         return this.active;
10523     },
10524
10525     /**
10526      * Updates the tab body element to fit the height of the container element
10527      * for overflow scrolling
10528      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10529      */
10530     syncHeight : function(targetHeight){
10531         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532         var bm = this.bodyEl.getMargins();
10533         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534         this.bodyEl.setHeight(newHeight);
10535         return newHeight;
10536     },
10537
10538     onResize : function(){
10539         if(this.monitorResize){
10540             this.autoSizeTabs();
10541         }
10542     },
10543
10544     /**
10545      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10546      */
10547     beginUpdate : function(){
10548         this.updating = true;
10549     },
10550
10551     /**
10552      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     endUpdate : function(){
10555         this.updating = false;
10556         this.autoSizeTabs();
10557     },
10558
10559     /**
10560      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10561      */
10562     autoSizeTabs : function(){
10563         var count = this.items.length;
10564         var vcount = count - this.hiddenCount;
10565         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567         var availWidth = Math.floor(w / vcount);
10568         var b = this.stripBody;
10569         if(b.getWidth() > w){
10570             var tabs = this.items;
10571             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572             if(availWidth < this.minTabWidth){
10573                 /*if(!this.sleft){    // incomplete scrolling code
10574                     this.createScrollButtons();
10575                 }
10576                 this.showScroll();
10577                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10578             }
10579         }else{
10580             if(this.currentTabWidth < this.preferredTabWidth){
10581                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10582             }
10583         }
10584     },
10585
10586     /**
10587      * Returns the number of tabs in this TabPanel.
10588      * @return {Number}
10589      */
10590      getCount : function(){
10591          return this.items.length;
10592      },
10593
10594     /**
10595      * Resizes all the tabs to the passed width
10596      * @param {Number} The new width
10597      */
10598     setTabWidth : function(width){
10599         this.currentTabWidth = width;
10600         for(var i = 0, len = this.items.length; i < len; i++) {
10601                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10602         }
10603     },
10604
10605     /**
10606      * Destroys this TabPanel
10607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10608      */
10609     destroy : function(removeEl){
10610         Roo.EventManager.removeResizeListener(this.onResize, this);
10611         for(var i = 0, len = this.items.length; i < len; i++){
10612             this.items[i].purgeListeners();
10613         }
10614         if(removeEl === true){
10615             this.el.update("");
10616             this.el.remove();
10617         }
10618     }
10619 });
10620
10621 /**
10622  * @class Roo.TabPanelItem
10623  * @extends Roo.util.Observable
10624  * Represents an individual item (tab plus body) in a TabPanel.
10625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626  * @param {String} id The id of this TabPanelItem
10627  * @param {String} text The text for the tab of this TabPanelItem
10628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10629  */
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10631     /**
10632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633      * @type Roo.TabPanel
10634      */
10635     this.tabPanel = tabPanel;
10636     /**
10637      * The id for this TabPanelItem
10638      * @type String
10639      */
10640     this.id = id;
10641     /** @private */
10642     this.disabled = false;
10643     /** @private */
10644     this.text = text;
10645     /** @private */
10646     this.loaded = false;
10647     this.closable = closable;
10648
10649     /**
10650      * The body element for this TabPanelItem.
10651      * @type Roo.Element
10652      */
10653     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655     this.bodyEl.setStyle("display", "block");
10656     this.bodyEl.setStyle("zoom", "1");
10657     this.hideAction();
10658
10659     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10660     /** @private */
10661     this.el = Roo.get(els.el, true);
10662     this.inner = Roo.get(els.inner, true);
10663     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664     this.pnode = Roo.get(els.el.parentNode, true);
10665     this.el.on("mousedown", this.onTabMouseDown, this);
10666     this.el.on("click", this.onTabClick, this);
10667     /** @private */
10668     if(closable){
10669         var c = Roo.get(els.close, true);
10670         c.dom.title = this.closeText;
10671         c.addClassOnOver("close-over");
10672         c.on("click", this.closeClick, this);
10673      }
10674
10675     this.addEvents({
10676          /**
10677          * @event activate
10678          * Fires when this tab becomes the active tab.
10679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680          * @param {Roo.TabPanelItem} this
10681          */
10682         "activate": true,
10683         /**
10684          * @event beforeclose
10685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686          * @param {Roo.TabPanelItem} this
10687          * @param {Object} e Set cancel to true on this object to cancel the close.
10688          */
10689         "beforeclose": true,
10690         /**
10691          * @event close
10692          * Fires when this tab is closed.
10693          * @param {Roo.TabPanelItem} this
10694          */
10695          "close": true,
10696         /**
10697          * @event deactivate
10698          * Fires when this tab is no longer the active tab.
10699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700          * @param {Roo.TabPanelItem} this
10701          */
10702          "deactivate" : true
10703     });
10704     this.hidden = false;
10705
10706     Roo.TabPanelItem.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710     purgeListeners : function(){
10711        Roo.util.Observable.prototype.purgeListeners.call(this);
10712        this.el.removeAllListeners();
10713     },
10714     /**
10715      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10716      */
10717     show : function(){
10718         this.pnode.addClass("on");
10719         this.showAction();
10720         if(Roo.isOpera){
10721             this.tabPanel.stripWrap.repaint();
10722         }
10723         this.fireEvent("activate", this.tabPanel, this);
10724     },
10725
10726     /**
10727      * Returns true if this tab is the active tab.
10728      * @return {Boolean}
10729      */
10730     isActive : function(){
10731         return this.tabPanel.getActiveTab() == this;
10732     },
10733
10734     /**
10735      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10736      */
10737     hide : function(){
10738         this.pnode.removeClass("on");
10739         this.hideAction();
10740         this.fireEvent("deactivate", this.tabPanel, this);
10741     },
10742
10743     hideAction : function(){
10744         this.bodyEl.hide();
10745         this.bodyEl.setStyle("position", "absolute");
10746         this.bodyEl.setLeft("-20000px");
10747         this.bodyEl.setTop("-20000px");
10748     },
10749
10750     showAction : function(){
10751         this.bodyEl.setStyle("position", "relative");
10752         this.bodyEl.setTop("");
10753         this.bodyEl.setLeft("");
10754         this.bodyEl.show();
10755     },
10756
10757     /**
10758      * Set the tooltip for the tab.
10759      * @param {String} tooltip The tab's tooltip
10760      */
10761     setTooltip : function(text){
10762         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763             this.textEl.dom.qtip = text;
10764             this.textEl.dom.removeAttribute('title');
10765         }else{
10766             this.textEl.dom.title = text;
10767         }
10768     },
10769
10770     onTabClick : function(e){
10771         e.preventDefault();
10772         this.tabPanel.activate(this.id);
10773     },
10774
10775     onTabMouseDown : function(e){
10776         e.preventDefault();
10777         this.tabPanel.activate(this.id);
10778     },
10779
10780     getWidth : function(){
10781         return this.inner.getWidth();
10782     },
10783
10784     setWidth : function(width){
10785         var iwidth = width - this.pnode.getPadding("lr");
10786         this.inner.setWidth(iwidth);
10787         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788         this.pnode.setWidth(width);
10789     },
10790
10791     /**
10792      * Show or hide the tab
10793      * @param {Boolean} hidden True to hide or false to show.
10794      */
10795     setHidden : function(hidden){
10796         this.hidden = hidden;
10797         this.pnode.setStyle("display", hidden ? "none" : "");
10798     },
10799
10800     /**
10801      * Returns true if this tab is "hidden"
10802      * @return {Boolean}
10803      */
10804     isHidden : function(){
10805         return this.hidden;
10806     },
10807
10808     /**
10809      * Returns the text for this tab
10810      * @return {String}
10811      */
10812     getText : function(){
10813         return this.text;
10814     },
10815
10816     autoSize : function(){
10817         //this.el.beginMeasure();
10818         this.textEl.setWidth(1);
10819         /*
10820          *  #2804 [new] Tabs in Roojs
10821          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10822          */
10823         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824         //this.el.endMeasure();
10825     },
10826
10827     /**
10828      * Sets the text for the tab (Note: this also sets the tooltip text)
10829      * @param {String} text The tab's text and tooltip
10830      */
10831     setText : function(text){
10832         this.text = text;
10833         this.textEl.update(text);
10834         this.setTooltip(text);
10835         if(!this.tabPanel.resizeTabs){
10836             this.autoSize();
10837         }
10838     },
10839     /**
10840      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10841      */
10842     activate : function(){
10843         this.tabPanel.activate(this.id);
10844     },
10845
10846     /**
10847      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10848      */
10849     disable : function(){
10850         if(this.tabPanel.active != this){
10851             this.disabled = true;
10852             this.pnode.addClass("disabled");
10853         }
10854     },
10855
10856     /**
10857      * Enables this TabPanelItem if it was previously disabled.
10858      */
10859     enable : function(){
10860         this.disabled = false;
10861         this.pnode.removeClass("disabled");
10862     },
10863
10864     /**
10865      * Sets the content for this TabPanelItem.
10866      * @param {String} content The content
10867      * @param {Boolean} loadScripts true to look for and load scripts
10868      */
10869     setContent : function(content, loadScripts){
10870         this.bodyEl.update(content, loadScripts);
10871     },
10872
10873     /**
10874      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875      * @return {Roo.UpdateManager} The UpdateManager
10876      */
10877     getUpdateManager : function(){
10878         return this.bodyEl.getUpdateManager();
10879     },
10880
10881     /**
10882      * Set a URL to be used to load the content for this TabPanelItem.
10883      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884      * @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)
10885      * @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)
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     setUrl : function(url, params, loadOnce){
10889         if(this.refreshDelegate){
10890             this.un('activate', this.refreshDelegate);
10891         }
10892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893         this.on("activate", this.refreshDelegate);
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /** @private */
10898     _handleRefresh : function(url, params, loadOnce){
10899         if(!loadOnce || !this.loaded){
10900             var updater = this.bodyEl.getUpdateManager();
10901             updater.update(url, params, this._setLoaded.createDelegate(this));
10902         }
10903     },
10904
10905     /**
10906      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907      *   Will fail silently if the setUrl method has not been called.
10908      *   This does not activate the panel, just updates its content.
10909      */
10910     refresh : function(){
10911         if(this.refreshDelegate){
10912            this.loaded = false;
10913            this.refreshDelegate();
10914         }
10915     },
10916
10917     /** @private */
10918     _setLoaded : function(){
10919         this.loaded = true;
10920     },
10921
10922     /** @private */
10923     closeClick : function(e){
10924         var o = {};
10925         e.stopEvent();
10926         this.fireEvent("beforeclose", this, o);
10927         if(o.cancel !== true){
10928             this.tabPanel.removeTab(this.id);
10929         }
10930     },
10931     /**
10932      * The text displayed in the tooltip for the close icon.
10933      * @type String
10934      */
10935     closeText : "Close this tab"
10936 });
10937
10938 /** @private */
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940     var strip = document.createElement("div");
10941     strip.className = "x-tabs-wrap";
10942     container.appendChild(strip);
10943     return strip;
10944 };
10945 /** @private */
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947     // div wrapper for retard IE
10948     // returns the "tr" element.
10949     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952     return strip.firstChild.firstChild.firstChild.firstChild;
10953 };
10954 /** @private */
10955 Roo.TabPanel.prototype.createBody = function(container){
10956     var body = document.createElement("div");
10957     Roo.id(body, "tab-body");
10958     Roo.fly(body).addClass("x-tabs-body");
10959     container.appendChild(body);
10960     return body;
10961 };
10962 /** @private */
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964     var body = Roo.getDom(id);
10965     if(!body){
10966         body = document.createElement("div");
10967         body.id = id;
10968     }
10969     Roo.fly(body).addClass("x-tabs-item-body");
10970     bodyEl.insertBefore(body, bodyEl.firstChild);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975     var td = document.createElement("td");
10976     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977     //stripEl.appendChild(td);
10978     if(closable){
10979         td.className = "x-tabs-closable";
10980         if(!this.closeTpl){
10981             this.closeTpl = new Roo.Template(
10982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10985             );
10986         }
10987         var el = this.closeTpl.overwrite(td, {"text": text});
10988         var close = el.getElementsByTagName("div")[0];
10989         var inner = el.getElementsByTagName("em")[0];
10990         return {"el": el, "close": close, "inner": inner};
10991     } else {
10992         if(!this.tabTpl){
10993             this.tabTpl = new Roo.Template(
10994                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10996             );
10997         }
10998         var el = this.tabTpl.overwrite(td, {"text": text});
10999         var inner = el.getElementsByTagName("em")[0];
11000         return {"el": el, "inner": inner};
11001     }
11002 };/*
11003  * Based on:
11004  * Ext JS Library 1.1.1
11005  * Copyright(c) 2006-2007, Ext JS, LLC.
11006  *
11007  * Originally Released Under LGPL - original licence link has changed is not relivant.
11008  *
11009  * Fork - LGPL
11010  * <script type="text/javascript">
11011  */
11012
11013 /**
11014  * @class Roo.Button
11015  * @extends Roo.util.Observable
11016  * Simple Button class
11017  * @cfg {String} text The button text
11018  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021  * @cfg {Object} scope The scope of the handler
11022  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028    applies if enableToggle = true)
11029  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11032  * @constructor
11033  * Create a new button
11034  * @param {Object} config The config object
11035  */
11036 Roo.Button = function(renderTo, config)
11037 {
11038     if (!config) {
11039         config = renderTo;
11040         renderTo = config.renderTo || false;
11041     }
11042     
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046              * @event click
11047              * Fires when this button is clicked
11048              * @param {Button} this
11049              * @param {EventObject} e The click event
11050              */
11051             "click" : true,
11052         /**
11053              * @event toggle
11054              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055              * @param {Button} this
11056              * @param {Boolean} pressed
11057              */
11058             "toggle" : true,
11059         /**
11060              * @event mouseover
11061              * Fires when the mouse hovers over the button
11062              * @param {Button} this
11063              * @param {Event} e The event object
11064              */
11065         'mouseover' : true,
11066         /**
11067              * @event mouseout
11068              * Fires when the mouse exits the button
11069              * @param {Button} this
11070              * @param {Event} e The event object
11071              */
11072         'mouseout': true,
11073          /**
11074              * @event render
11075              * Fires when the button is rendered
11076              * @param {Button} this
11077              */
11078         'render': true
11079     });
11080     if(this.menu){
11081         this.menu = Roo.menu.MenuMgr.get(this.menu);
11082     }
11083     // register listeners first!!  - so render can be captured..
11084     Roo.util.Observable.call(this);
11085     if(renderTo){
11086         this.render(renderTo);
11087     }
11088     
11089   
11090 };
11091
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11093     /**
11094      * 
11095      */
11096     
11097     /**
11098      * Read-only. True if this button is hidden
11099      * @type Boolean
11100      */
11101     hidden : false,
11102     /**
11103      * Read-only. True if this button is disabled
11104      * @type Boolean
11105      */
11106     disabled : false,
11107     /**
11108      * Read-only. True if this button is pressed (only if enableToggle = true)
11109      * @type Boolean
11110      */
11111     pressed : false,
11112
11113     /**
11114      * @cfg {Number} tabIndex 
11115      * The DOM tabIndex for this button (defaults to undefined)
11116      */
11117     tabIndex : undefined,
11118
11119     /**
11120      * @cfg {Boolean} enableToggle
11121      * True to enable pressed/not pressed toggling (defaults to false)
11122      */
11123     enableToggle: false,
11124     /**
11125      * @cfg {Mixed} menu
11126      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11127      */
11128     menu : undefined,
11129     /**
11130      * @cfg {String} menuAlign
11131      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11132      */
11133     menuAlign : "tl-bl?",
11134
11135     /**
11136      * @cfg {String} iconCls
11137      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11138      */
11139     iconCls : undefined,
11140     /**
11141      * @cfg {String} type
11142      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11143      */
11144     type : 'button',
11145
11146     // private
11147     menuClassTarget: 'tr',
11148
11149     /**
11150      * @cfg {String} clickEvent
11151      * The type of event to map to the button's event handler (defaults to 'click')
11152      */
11153     clickEvent : 'click',
11154
11155     /**
11156      * @cfg {Boolean} handleMouseEvents
11157      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11158      */
11159     handleMouseEvents : true,
11160
11161     /**
11162      * @cfg {String} tooltipType
11163      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11164      */
11165     tooltipType : 'qtip',
11166
11167     /**
11168      * @cfg {String} cls
11169      * A CSS class to apply to the button's main element.
11170      */
11171     
11172     /**
11173      * @cfg {Roo.Template} template (Optional)
11174      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176      * require code modifications if required elements (e.g. a button) aren't present.
11177      */
11178
11179     // private
11180     render : function(renderTo){
11181         var btn;
11182         if(this.hideParent){
11183             this.parentEl = Roo.get(renderTo);
11184         }
11185         if(!this.dhconfig){
11186             if(!this.template){
11187                 if(!Roo.Button.buttonTemplate){
11188                     // hideous table template
11189                     Roo.Button.buttonTemplate = new Roo.Template(
11190                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191                         '<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>',
11192                         "</tr></tbody></table>");
11193                 }
11194                 this.template = Roo.Button.buttonTemplate;
11195             }
11196             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11197             var btnEl = btn.child("button:first");
11198             btnEl.on('focus', this.onFocus, this);
11199             btnEl.on('blur', this.onBlur, this);
11200             if(this.cls){
11201                 btn.addClass(this.cls);
11202             }
11203             if(this.icon){
11204                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11205             }
11206             if(this.iconCls){
11207                 btnEl.addClass(this.iconCls);
11208                 if(!this.cls){
11209                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11210                 }
11211             }
11212             if(this.tabIndex !== undefined){
11213                 btnEl.dom.tabIndex = this.tabIndex;
11214             }
11215             if(this.tooltip){
11216                 if(typeof this.tooltip == 'object'){
11217                     Roo.QuickTips.tips(Roo.apply({
11218                           target: btnEl.id
11219                     }, this.tooltip));
11220                 } else {
11221                     btnEl.dom[this.tooltipType] = this.tooltip;
11222                 }
11223             }
11224         }else{
11225             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11226         }
11227         this.el = btn;
11228         if(this.id){
11229             this.el.dom.id = this.el.id = this.id;
11230         }
11231         if(this.menu){
11232             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233             this.menu.on("show", this.onMenuShow, this);
11234             this.menu.on("hide", this.onMenuHide, this);
11235         }
11236         btn.addClass("x-btn");
11237         if(Roo.isIE && !Roo.isIE7){
11238             this.autoWidth.defer(1, this);
11239         }else{
11240             this.autoWidth();
11241         }
11242         if(this.handleMouseEvents){
11243             btn.on("mouseover", this.onMouseOver, this);
11244             btn.on("mouseout", this.onMouseOut, this);
11245             btn.on("mousedown", this.onMouseDown, this);
11246         }
11247         btn.on(this.clickEvent, this.onClick, this);
11248         //btn.on("mouseup", this.onMouseUp, this);
11249         if(this.hidden){
11250             this.hide();
11251         }
11252         if(this.disabled){
11253             this.disable();
11254         }
11255         Roo.ButtonToggleMgr.register(this);
11256         if(this.pressed){
11257             this.el.addClass("x-btn-pressed");
11258         }
11259         if(this.repeat){
11260             var repeater = new Roo.util.ClickRepeater(btn,
11261                 typeof this.repeat == "object" ? this.repeat : {}
11262             );
11263             repeater.on("click", this.onClick,  this);
11264         }
11265         
11266         this.fireEvent('render', this);
11267         
11268     },
11269     /**
11270      * Returns the button's underlying element
11271      * @return {Roo.Element} The element
11272      */
11273     getEl : function(){
11274         return this.el;  
11275     },
11276     
11277     /**
11278      * Destroys this Button and removes any listeners.
11279      */
11280     destroy : function(){
11281         Roo.ButtonToggleMgr.unregister(this);
11282         this.el.removeAllListeners();
11283         this.purgeListeners();
11284         this.el.remove();
11285     },
11286
11287     // private
11288     autoWidth : function(){
11289         if(this.el){
11290             this.el.setWidth("auto");
11291             if(Roo.isIE7 && Roo.isStrict){
11292                 var ib = this.el.child('button');
11293                 if(ib && ib.getWidth() > 20){
11294                     ib.clip();
11295                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11296                 }
11297             }
11298             if(this.minWidth){
11299                 if(this.hidden){
11300                     this.el.beginMeasure();
11301                 }
11302                 if(this.el.getWidth() < this.minWidth){
11303                     this.el.setWidth(this.minWidth);
11304                 }
11305                 if(this.hidden){
11306                     this.el.endMeasure();
11307                 }
11308             }
11309         }
11310     },
11311
11312     /**
11313      * Assigns this button's click handler
11314      * @param {Function} handler The function to call when the button is clicked
11315      * @param {Object} scope (optional) Scope for the function passed in
11316      */
11317     setHandler : function(handler, scope){
11318         this.handler = handler;
11319         this.scope = scope;  
11320     },
11321     
11322     /**
11323      * Sets this button's text
11324      * @param {String} text The button text
11325      */
11326     setText : function(text){
11327         this.text = text;
11328         if(this.el){
11329             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11330         }
11331         this.autoWidth();
11332     },
11333     
11334     /**
11335      * Gets the text for this button
11336      * @return {String} The button text
11337      */
11338     getText : function(){
11339         return this.text;  
11340     },
11341     
11342     /**
11343      * Show this button
11344      */
11345     show: function(){
11346         this.hidden = false;
11347         if(this.el){
11348             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11349         }
11350     },
11351     
11352     /**
11353      * Hide this button
11354      */
11355     hide: function(){
11356         this.hidden = true;
11357         if(this.el){
11358             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11359         }
11360     },
11361     
11362     /**
11363      * Convenience function for boolean show/hide
11364      * @param {Boolean} visible True to show, false to hide
11365      */
11366     setVisible: function(visible){
11367         if(visible) {
11368             this.show();
11369         }else{
11370             this.hide();
11371         }
11372     },
11373     
11374     /**
11375      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376      * @param {Boolean} state (optional) Force a particular state
11377      */
11378     toggle : function(state){
11379         state = state === undefined ? !this.pressed : state;
11380         if(state != this.pressed){
11381             if(state){
11382                 this.el.addClass("x-btn-pressed");
11383                 this.pressed = true;
11384                 this.fireEvent("toggle", this, true);
11385             }else{
11386                 this.el.removeClass("x-btn-pressed");
11387                 this.pressed = false;
11388                 this.fireEvent("toggle", this, false);
11389             }
11390             if(this.toggleHandler){
11391                 this.toggleHandler.call(this.scope || this, this, state);
11392             }
11393         }
11394     },
11395     
11396     /**
11397      * Focus the button
11398      */
11399     focus : function(){
11400         this.el.child('button:first').focus();
11401     },
11402     
11403     /**
11404      * Disable this button
11405      */
11406     disable : function(){
11407         if(this.el){
11408             this.el.addClass("x-btn-disabled");
11409         }
11410         this.disabled = true;
11411     },
11412     
11413     /**
11414      * Enable this button
11415      */
11416     enable : function(){
11417         if(this.el){
11418             this.el.removeClass("x-btn-disabled");
11419         }
11420         this.disabled = false;
11421     },
11422
11423     /**
11424      * Convenience function for boolean enable/disable
11425      * @param {Boolean} enabled True to enable, false to disable
11426      */
11427     setDisabled : function(v){
11428         this[v !== true ? "enable" : "disable"]();
11429     },
11430
11431     // private
11432     onClick : function(e){
11433         if(e){
11434             e.preventDefault();
11435         }
11436         if(e.button != 0){
11437             return;
11438         }
11439         if(!this.disabled){
11440             if(this.enableToggle){
11441                 this.toggle();
11442             }
11443             if(this.menu && !this.menu.isVisible()){
11444                 this.menu.show(this.el, this.menuAlign);
11445             }
11446             this.fireEvent("click", this, e);
11447             if(this.handler){
11448                 this.el.removeClass("x-btn-over");
11449                 this.handler.call(this.scope || this, this, e);
11450             }
11451         }
11452     },
11453     // private
11454     onMouseOver : function(e){
11455         if(!this.disabled){
11456             this.el.addClass("x-btn-over");
11457             this.fireEvent('mouseover', this, e);
11458         }
11459     },
11460     // private
11461     onMouseOut : function(e){
11462         if(!e.within(this.el,  true)){
11463             this.el.removeClass("x-btn-over");
11464             this.fireEvent('mouseout', this, e);
11465         }
11466     },
11467     // private
11468     onFocus : function(e){
11469         if(!this.disabled){
11470             this.el.addClass("x-btn-focus");
11471         }
11472     },
11473     // private
11474     onBlur : function(e){
11475         this.el.removeClass("x-btn-focus");
11476     },
11477     // private
11478     onMouseDown : function(e){
11479         if(!this.disabled && e.button == 0){
11480             this.el.addClass("x-btn-click");
11481             Roo.get(document).on('mouseup', this.onMouseUp, this);
11482         }
11483     },
11484     // private
11485     onMouseUp : function(e){
11486         if(e.button == 0){
11487             this.el.removeClass("x-btn-click");
11488             Roo.get(document).un('mouseup', this.onMouseUp, this);
11489         }
11490     },
11491     // private
11492     onMenuShow : function(e){
11493         this.el.addClass("x-btn-menu-active");
11494     },
11495     // private
11496     onMenuHide : function(e){
11497         this.el.removeClass("x-btn-menu-active");
11498     }   
11499 });
11500
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11503    var groups = {};
11504    
11505    function toggleGroup(btn, state){
11506        if(state){
11507            var g = groups[btn.toggleGroup];
11508            for(var i = 0, l = g.length; i < l; i++){
11509                if(g[i] != btn){
11510                    g[i].toggle(false);
11511                }
11512            }
11513        }
11514    }
11515    
11516    return {
11517        register : function(btn){
11518            if(!btn.toggleGroup){
11519                return;
11520            }
11521            var g = groups[btn.toggleGroup];
11522            if(!g){
11523                g = groups[btn.toggleGroup] = [];
11524            }
11525            g.push(btn);
11526            btn.on("toggle", toggleGroup);
11527        },
11528        
11529        unregister : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(g){
11535                g.remove(btn);
11536                btn.un("toggle", toggleGroup);
11537            }
11538        }
11539    };
11540 }();/*
11541  * Based on:
11542  * Ext JS Library 1.1.1
11543  * Copyright(c) 2006-2007, Ext JS, LLC.
11544  *
11545  * Originally Released Under LGPL - original licence link has changed is not relivant.
11546  *
11547  * Fork - LGPL
11548  * <script type="text/javascript">
11549  */
11550  
11551 /**
11552  * @class Roo.SplitButton
11553  * @extends Roo.Button
11554  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11556  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558  * @cfg {String} arrowTooltip The title attribute of the arrow
11559  * @constructor
11560  * Create a new menu button
11561  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562  * @param {Object} config The config object
11563  */
11564 Roo.SplitButton = function(renderTo, config){
11565     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11566     /**
11567      * @event arrowclick
11568      * Fires when this button's arrow is clicked
11569      * @param {SplitButton} this
11570      * @param {EventObject} e The click event
11571      */
11572     this.addEvents({"arrowclick":true});
11573 };
11574
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576     render : function(renderTo){
11577         // this is one sweet looking template!
11578         var tpl = new Roo.Template(
11579             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581             '<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>',
11582             "</tbody></table></td><td>",
11583             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584             '<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>',
11585             "</tbody></table></td></tr></table>"
11586         );
11587         var btn = tpl.append(renderTo, [this.text, this.type], true);
11588         var btnEl = btn.child("button");
11589         if(this.cls){
11590             btn.addClass(this.cls);
11591         }
11592         if(this.icon){
11593             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11594         }
11595         if(this.iconCls){
11596             btnEl.addClass(this.iconCls);
11597             if(!this.cls){
11598                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11599             }
11600         }
11601         this.el = btn;
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606             btn.on("mouseup", this.onMouseUp, this);
11607         }
11608         btn.on(this.clickEvent, this.onClick, this);
11609         if(this.tooltip){
11610             if(typeof this.tooltip == 'object'){
11611                 Roo.QuickTips.tips(Roo.apply({
11612                       target: btnEl.id
11613                 }, this.tooltip));
11614             } else {
11615                 btnEl.dom[this.tooltipType] = this.tooltip;
11616             }
11617         }
11618         if(this.arrowTooltip){
11619             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11620         }
11621         if(this.hidden){
11622             this.hide();
11623         }
11624         if(this.disabled){
11625             this.disable();
11626         }
11627         if(this.pressed){
11628             this.el.addClass("x-btn-pressed");
11629         }
11630         if(Roo.isIE && !Roo.isIE7){
11631             this.autoWidth.defer(1, this);
11632         }else{
11633             this.autoWidth();
11634         }
11635         if(this.menu){
11636             this.menu.on("show", this.onMenuShow, this);
11637             this.menu.on("hide", this.onMenuHide, this);
11638         }
11639         this.fireEvent('render', this);
11640     },
11641
11642     // private
11643     autoWidth : function(){
11644         if(this.el){
11645             var tbl = this.el.child("table:first");
11646             var tbl2 = this.el.child("table:last");
11647             this.el.setWidth("auto");
11648             tbl.setWidth("auto");
11649             if(Roo.isIE7 && Roo.isStrict){
11650                 var ib = this.el.child('button:first');
11651                 if(ib && ib.getWidth() > 20){
11652                     ib.clip();
11653                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11654                 }
11655             }
11656             if(this.minWidth){
11657                 if(this.hidden){
11658                     this.el.beginMeasure();
11659                 }
11660                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11662                 }
11663                 if(this.hidden){
11664                     this.el.endMeasure();
11665                 }
11666             }
11667             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11668         } 
11669     },
11670     /**
11671      * Sets this button's click handler
11672      * @param {Function} handler The function to call when the button is clicked
11673      * @param {Object} scope (optional) Scope for the function passed above
11674      */
11675     setHandler : function(handler, scope){
11676         this.handler = handler;
11677         this.scope = scope;  
11678     },
11679     
11680     /**
11681      * Sets this button's arrow click handler
11682      * @param {Function} handler The function to call when the arrow is clicked
11683      * @param {Object} scope (optional) Scope for the function passed above
11684      */
11685     setArrowHandler : function(handler, scope){
11686         this.arrowHandler = handler;
11687         this.scope = scope;  
11688     },
11689     
11690     /**
11691      * Focus the button
11692      */
11693     focus : function(){
11694         if(this.el){
11695             this.el.child("button:first").focus();
11696         }
11697     },
11698
11699     // private
11700     onClick : function(e){
11701         e.preventDefault();
11702         if(!this.disabled){
11703             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704                 if(this.menu && !this.menu.isVisible()){
11705                     this.menu.show(this.el, this.menuAlign);
11706                 }
11707                 this.fireEvent("arrowclick", this, e);
11708                 if(this.arrowHandler){
11709                     this.arrowHandler.call(this.scope || this, this, e);
11710                 }
11711             }else{
11712                 this.fireEvent("click", this, e);
11713                 if(this.handler){
11714                     this.handler.call(this.scope || this, this, e);
11715                 }
11716             }
11717         }
11718     },
11719     // private
11720     onMouseDown : function(e){
11721         if(!this.disabled){
11722             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11723         }
11724     },
11725     // private
11726     onMouseUp : function(e){
11727         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11728     }   
11729 });
11730
11731
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11734  * Based on:
11735  * Ext JS Library 1.1.1
11736  * Copyright(c) 2006-2007, Ext JS, LLC.
11737  *
11738  * Originally Released Under LGPL - original licence link has changed is not relivant.
11739  *
11740  * Fork - LGPL
11741  * <script type="text/javascript">
11742  */
11743
11744 /**
11745  * @class Roo.Toolbar
11746  * Basic Toolbar class.
11747  * @constructor
11748  * Creates a new Toolbar
11749  * @param {Object} container The config object
11750  */ 
11751 Roo.Toolbar = function(container, buttons, config)
11752 {
11753     /// old consturctor format still supported..
11754     if(container instanceof Array){ // omit the container for later rendering
11755         buttons = container;
11756         config = buttons;
11757         container = null;
11758     }
11759     if (typeof(container) == 'object' && container.xtype) {
11760         config = container;
11761         container = config.container;
11762         buttons = config.buttons || []; // not really - use items!!
11763     }
11764     var xitems = [];
11765     if (config && config.items) {
11766         xitems = config.items;
11767         delete config.items;
11768     }
11769     Roo.apply(this, config);
11770     this.buttons = buttons;
11771     
11772     if(container){
11773         this.render(container);
11774     }
11775     this.xitems = xitems;
11776     Roo.each(xitems, function(b) {
11777         this.add(b);
11778     }, this);
11779     
11780 };
11781
11782 Roo.Toolbar.prototype = {
11783     /**
11784      * @cfg {Array} items
11785      * array of button configs or elements to add (will be converted to a MixedCollection)
11786      */
11787     
11788     /**
11789      * @cfg {String/HTMLElement/Element} container
11790      * The id or element that will contain the toolbar
11791      */
11792     // private
11793     render : function(ct){
11794         this.el = Roo.get(ct);
11795         if(this.cls){
11796             this.el.addClass(this.cls);
11797         }
11798         // using a table allows for vertical alignment
11799         // 100% width is needed by Safari...
11800         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801         this.tr = this.el.child("tr", true);
11802         var autoId = 0;
11803         this.items = new Roo.util.MixedCollection(false, function(o){
11804             return o.id || ("item" + (++autoId));
11805         });
11806         if(this.buttons){
11807             this.add.apply(this, this.buttons);
11808             delete this.buttons;
11809         }
11810     },
11811
11812     /**
11813      * Adds element(s) to the toolbar -- this function takes a variable number of 
11814      * arguments of mixed type and adds them to the toolbar.
11815      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11816      * <ul>
11817      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11826      * </ul>
11827      * @param {Mixed} arg2
11828      * @param {Mixed} etc.
11829      */
11830     add : function(){
11831         var a = arguments, l = a.length;
11832         for(var i = 0; i < l; i++){
11833             this._add(a[i]);
11834         }
11835     },
11836     // private..
11837     _add : function(el) {
11838         
11839         if (el.xtype) {
11840             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11841         }
11842         
11843         if (el.applyTo){ // some kind of form field
11844             return this.addField(el);
11845         } 
11846         if (el.render){ // some kind of Toolbar.Item
11847             return this.addItem(el);
11848         }
11849         if (typeof el == "string"){ // string
11850             if(el == "separator" || el == "-"){
11851                 return this.addSeparator();
11852             }
11853             if (el == " "){
11854                 return this.addSpacer();
11855             }
11856             if(el == "->"){
11857                 return this.addFill();
11858             }
11859             return this.addText(el);
11860             
11861         }
11862         if(el.tagName){ // element
11863             return this.addElement(el);
11864         }
11865         if(typeof el == "object"){ // must be button config?
11866             return this.addButton(el);
11867         }
11868         // and now what?!?!
11869         return false;
11870         
11871     },
11872     
11873     /**
11874      * Add an Xtype element
11875      * @param {Object} xtype Xtype Object
11876      * @return {Object} created Object
11877      */
11878     addxtype : function(e){
11879         return this.add(e);  
11880     },
11881     
11882     /**
11883      * Returns the Element for this toolbar.
11884      * @return {Roo.Element}
11885      */
11886     getEl : function(){
11887         return this.el;  
11888     },
11889     
11890     /**
11891      * Adds a separator
11892      * @return {Roo.Toolbar.Item} The separator item
11893      */
11894     addSeparator : function(){
11895         return this.addItem(new Roo.Toolbar.Separator());
11896     },
11897
11898     /**
11899      * Adds a spacer element
11900      * @return {Roo.Toolbar.Spacer} The spacer item
11901      */
11902     addSpacer : function(){
11903         return this.addItem(new Roo.Toolbar.Spacer());
11904     },
11905
11906     /**
11907      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908      * @return {Roo.Toolbar.Fill} The fill item
11909      */
11910     addFill : function(){
11911         return this.addItem(new Roo.Toolbar.Fill());
11912     },
11913
11914     /**
11915      * Adds any standard HTML element to the toolbar
11916      * @param {String/HTMLElement/Element} el The element or id of the element to add
11917      * @return {Roo.Toolbar.Item} The element's item
11918      */
11919     addElement : function(el){
11920         return this.addItem(new Roo.Toolbar.Item(el));
11921     },
11922     /**
11923      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924      * @type Roo.util.MixedCollection  
11925      */
11926     items : false,
11927      
11928     /**
11929      * Adds any Toolbar.Item or subclass
11930      * @param {Roo.Toolbar.Item} item
11931      * @return {Roo.Toolbar.Item} The item
11932      */
11933     addItem : function(item){
11934         var td = this.nextBlock();
11935         item.render(td);
11936         this.items.add(item);
11937         return item;
11938     },
11939     
11940     /**
11941      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942      * @param {Object/Array} config A button config or array of configs
11943      * @return {Roo.Toolbar.Button/Array}
11944      */
11945     addButton : function(config){
11946         if(config instanceof Array){
11947             var buttons = [];
11948             for(var i = 0, len = config.length; i < len; i++) {
11949                 buttons.push(this.addButton(config[i]));
11950             }
11951             return buttons;
11952         }
11953         var b = config;
11954         if(!(config instanceof Roo.Toolbar.Button)){
11955             b = config.split ?
11956                 new Roo.Toolbar.SplitButton(config) :
11957                 new Roo.Toolbar.Button(config);
11958         }
11959         var td = this.nextBlock();
11960         b.render(td);
11961         this.items.add(b);
11962         return b;
11963     },
11964     
11965     /**
11966      * Adds text to the toolbar
11967      * @param {String} text The text to add
11968      * @return {Roo.Toolbar.Item} The element's item
11969      */
11970     addText : function(text){
11971         return this.addItem(new Roo.Toolbar.TextItem(text));
11972     },
11973     
11974     /**
11975      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976      * @param {Number} index The index where the item is to be inserted
11977      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978      * @return {Roo.Toolbar.Button/Item}
11979      */
11980     insertButton : function(index, item){
11981         if(item instanceof Array){
11982             var buttons = [];
11983             for(var i = 0, len = item.length; i < len; i++) {
11984                buttons.push(this.insertButton(index + i, item[i]));
11985             }
11986             return buttons;
11987         }
11988         if (!(item instanceof Roo.Toolbar.Button)){
11989            item = new Roo.Toolbar.Button(item);
11990         }
11991         var td = document.createElement("td");
11992         this.tr.insertBefore(td, this.tr.childNodes[index]);
11993         item.render(td);
11994         this.items.insert(index, item);
11995         return item;
11996     },
11997     
11998     /**
11999      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000      * @param {Object} config
12001      * @return {Roo.Toolbar.Item} The element's item
12002      */
12003     addDom : function(config, returnEl){
12004         var td = this.nextBlock();
12005         Roo.DomHelper.overwrite(td, config);
12006         var ti = new Roo.Toolbar.Item(td.firstChild);
12007         ti.render(td);
12008         this.items.add(ti);
12009         return ti;
12010     },
12011
12012     /**
12013      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014      * @type Roo.util.MixedCollection  
12015      */
12016     fields : false,
12017     
12018     /**
12019      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020      * Note: the field should not have been rendered yet. For a field that has already been
12021      * rendered, use {@link #addElement}.
12022      * @param {Roo.form.Field} field
12023      * @return {Roo.ToolbarItem}
12024      */
12025      
12026       
12027     addField : function(field) {
12028         if (!this.fields) {
12029             var autoId = 0;
12030             this.fields = new Roo.util.MixedCollection(false, function(o){
12031                 return o.id || ("item" + (++autoId));
12032             });
12033
12034         }
12035         
12036         var td = this.nextBlock();
12037         field.render(td);
12038         var ti = new Roo.Toolbar.Item(td.firstChild);
12039         ti.render(td);
12040         this.items.add(ti);
12041         this.fields.add(field);
12042         return ti;
12043     },
12044     /**
12045      * Hide the toolbar
12046      * @method hide
12047      */
12048      
12049       
12050     hide : function()
12051     {
12052         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053         this.el.child('div').hide();
12054     },
12055     /**
12056      * Show the toolbar
12057      * @method show
12058      */
12059     show : function()
12060     {
12061         this.el.child('div').show();
12062     },
12063       
12064     // private
12065     nextBlock : function(){
12066         var td = document.createElement("td");
12067         this.tr.appendChild(td);
12068         return td;
12069     },
12070
12071     // private
12072     destroy : function(){
12073         if(this.items){ // rendered?
12074             Roo.destroy.apply(Roo, this.items.items);
12075         }
12076         if(this.fields){ // rendered?
12077             Roo.destroy.apply(Roo, this.fields.items);
12078         }
12079         Roo.Element.uncache(this.el, this.tr);
12080     }
12081 };
12082
12083 /**
12084  * @class Roo.Toolbar.Item
12085  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12086  * @constructor
12087  * Creates a new Item
12088  * @param {HTMLElement} el 
12089  */
12090 Roo.Toolbar.Item = function(el){
12091     var cfg = {};
12092     if (typeof (el.xtype) != 'undefined') {
12093         cfg = el;
12094         el = cfg.el;
12095     }
12096     
12097     this.el = Roo.getDom(el);
12098     this.id = Roo.id(this.el);
12099     this.hidden = false;
12100     
12101     this.addEvents({
12102          /**
12103              * @event render
12104              * Fires when the button is rendered
12105              * @param {Button} this
12106              */
12107         'render': true
12108     });
12109     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12110 };
12111 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12112 //Roo.Toolbar.Item.prototype = {
12113     
12114     /**
12115      * Get this item's HTML Element
12116      * @return {HTMLElement}
12117      */
12118     getEl : function(){
12119        return this.el;  
12120     },
12121
12122     // private
12123     render : function(td){
12124         
12125          this.td = td;
12126         td.appendChild(this.el);
12127         
12128         this.fireEvent('render', this);
12129     },
12130     
12131     /**
12132      * Removes and destroys this item.
12133      */
12134     destroy : function(){
12135         this.td.parentNode.removeChild(this.td);
12136     },
12137     
12138     /**
12139      * Shows this item.
12140      */
12141     show: function(){
12142         this.hidden = false;
12143         this.td.style.display = "";
12144     },
12145     
12146     /**
12147      * Hides this item.
12148      */
12149     hide: function(){
12150         this.hidden = true;
12151         this.td.style.display = "none";
12152     },
12153     
12154     /**
12155      * Convenience function for boolean show/hide.
12156      * @param {Boolean} visible true to show/false to hide
12157      */
12158     setVisible: function(visible){
12159         if(visible) {
12160             this.show();
12161         }else{
12162             this.hide();
12163         }
12164     },
12165     
12166     /**
12167      * Try to focus this item.
12168      */
12169     focus : function(){
12170         Roo.fly(this.el).focus();
12171     },
12172     
12173     /**
12174      * Disables this item.
12175      */
12176     disable : function(){
12177         Roo.fly(this.td).addClass("x-item-disabled");
12178         this.disabled = true;
12179         this.el.disabled = true;
12180     },
12181     
12182     /**
12183      * Enables this item.
12184      */
12185     enable : function(){
12186         Roo.fly(this.td).removeClass("x-item-disabled");
12187         this.disabled = false;
12188         this.el.disabled = false;
12189     }
12190 });
12191
12192
12193 /**
12194  * @class Roo.Toolbar.Separator
12195  * @extends Roo.Toolbar.Item
12196  * A simple toolbar separator class
12197  * @constructor
12198  * Creates a new Separator
12199  */
12200 Roo.Toolbar.Separator = function(cfg){
12201     
12202     var s = document.createElement("span");
12203     s.className = "ytb-sep";
12204     if (cfg) {
12205         cfg.el = s;
12206     }
12207     
12208     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12209 };
12210 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12211     enable:Roo.emptyFn,
12212     disable:Roo.emptyFn,
12213     focus:Roo.emptyFn
12214 });
12215
12216 /**
12217  * @class Roo.Toolbar.Spacer
12218  * @extends Roo.Toolbar.Item
12219  * A simple element that adds extra horizontal space to a toolbar.
12220  * @constructor
12221  * Creates a new Spacer
12222  */
12223 Roo.Toolbar.Spacer = function(cfg){
12224     var s = document.createElement("div");
12225     s.className = "ytb-spacer";
12226     if (cfg) {
12227         cfg.el = s;
12228     }
12229     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12230 };
12231 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12232     enable:Roo.emptyFn,
12233     disable:Roo.emptyFn,
12234     focus:Roo.emptyFn
12235 });
12236
12237 /**
12238  * @class Roo.Toolbar.Fill
12239  * @extends Roo.Toolbar.Spacer
12240  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12241  * @constructor
12242  * Creates a new Spacer
12243  */
12244 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12245     // private
12246     render : function(td){
12247         td.style.width = '100%';
12248         Roo.Toolbar.Fill.superclass.render.call(this, td);
12249     }
12250 });
12251
12252 /**
12253  * @class Roo.Toolbar.TextItem
12254  * @extends Roo.Toolbar.Item
12255  * A simple class that renders text directly into a toolbar.
12256  * @constructor
12257  * Creates a new TextItem
12258  * @param {String} text
12259  */
12260 Roo.Toolbar.TextItem = function(cfg){
12261     if (typeof(cfg) == 'object') {
12262         text = cfg.text;
12263     }
12264     var s = document.createElement("span");
12265     s.className = "ytb-text";
12266     s.innerHTML = text;
12267     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12268 };
12269 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12270     
12271      
12272     enable:Roo.emptyFn,
12273     disable:Roo.emptyFn,
12274     focus:Roo.emptyFn
12275 });
12276
12277 /**
12278  * @class Roo.Toolbar.Button
12279  * @extends Roo.Button
12280  * A button that renders into a toolbar.
12281  * @constructor
12282  * Creates a new Button
12283  * @param {Object} config A standard {@link Roo.Button} config object
12284  */
12285 Roo.Toolbar.Button = function(config){
12286     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12287 };
12288 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12289     render : function(td){
12290         this.td = td;
12291         Roo.Toolbar.Button.superclass.render.call(this, td);
12292     },
12293     
12294     /**
12295      * Removes and destroys this button
12296      */
12297     destroy : function(){
12298         Roo.Toolbar.Button.superclass.destroy.call(this);
12299         this.td.parentNode.removeChild(this.td);
12300     },
12301     
12302     /**
12303      * Shows this button
12304      */
12305     show: function(){
12306         this.hidden = false;
12307         this.td.style.display = "";
12308     },
12309     
12310     /**
12311      * Hides this button
12312      */
12313     hide: function(){
12314         this.hidden = true;
12315         this.td.style.display = "none";
12316     },
12317
12318     /**
12319      * Disables this item
12320      */
12321     disable : function(){
12322         Roo.fly(this.td).addClass("x-item-disabled");
12323         this.disabled = true;
12324     },
12325
12326     /**
12327      * Enables this item
12328      */
12329     enable : function(){
12330         Roo.fly(this.td).removeClass("x-item-disabled");
12331         this.disabled = false;
12332     }
12333 });
12334 // backwards compat
12335 Roo.ToolbarButton = Roo.Toolbar.Button;
12336
12337 /**
12338  * @class Roo.Toolbar.SplitButton
12339  * @extends Roo.SplitButton
12340  * A menu button that renders into a toolbar.
12341  * @constructor
12342  * Creates a new SplitButton
12343  * @param {Object} config A standard {@link Roo.SplitButton} config object
12344  */
12345 Roo.Toolbar.SplitButton = function(config){
12346     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12347 };
12348 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12349     render : function(td){
12350         this.td = td;
12351         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12352     },
12353     
12354     /**
12355      * Removes and destroys this button
12356      */
12357     destroy : function(){
12358         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12359         this.td.parentNode.removeChild(this.td);
12360     },
12361     
12362     /**
12363      * Shows this button
12364      */
12365     show: function(){
12366         this.hidden = false;
12367         this.td.style.display = "";
12368     },
12369     
12370     /**
12371      * Hides this button
12372      */
12373     hide: function(){
12374         this.hidden = true;
12375         this.td.style.display = "none";
12376     }
12377 });
12378
12379 // backwards compat
12380 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12381  * Based on:
12382  * Ext JS Library 1.1.1
12383  * Copyright(c) 2006-2007, Ext JS, LLC.
12384  *
12385  * Originally Released Under LGPL - original licence link has changed is not relivant.
12386  *
12387  * Fork - LGPL
12388  * <script type="text/javascript">
12389  */
12390  
12391 /**
12392  * @class Roo.PagingToolbar
12393  * @extends Roo.Toolbar
12394  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12395  * @constructor
12396  * Create a new PagingToolbar
12397  * @param {Object} config The config object
12398  */
12399 Roo.PagingToolbar = function(el, ds, config)
12400 {
12401     // old args format still supported... - xtype is prefered..
12402     if (typeof(el) == 'object' && el.xtype) {
12403         // created from xtype...
12404         config = el;
12405         ds = el.dataSource;
12406         el = config.container;
12407     }
12408     var items = [];
12409     if (config.items) {
12410         items = config.items;
12411         config.items = [];
12412     }
12413     
12414     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12415     this.ds = ds;
12416     this.cursor = 0;
12417     this.renderButtons(this.el);
12418     this.bind(ds);
12419     
12420     // supprot items array.
12421    
12422     Roo.each(items, function(e) {
12423         this.add(Roo.factory(e));
12424     },this);
12425     
12426 };
12427
12428 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12429     /**
12430      * @cfg {Roo.data.Store} dataSource
12431      * The underlying data store providing the paged data
12432      */
12433     /**
12434      * @cfg {String/HTMLElement/Element} container
12435      * container The id or element that will contain the toolbar
12436      */
12437     /**
12438      * @cfg {Boolean} displayInfo
12439      * True to display the displayMsg (defaults to false)
12440      */
12441     /**
12442      * @cfg {Number} pageSize
12443      * The number of records to display per page (defaults to 20)
12444      */
12445     pageSize: 20,
12446     /**
12447      * @cfg {String} displayMsg
12448      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12449      */
12450     displayMsg : 'Displaying {0} - {1} of {2}',
12451     /**
12452      * @cfg {String} emptyMsg
12453      * The message to display when no records are found (defaults to "No data to display")
12454      */
12455     emptyMsg : 'No data to display',
12456     /**
12457      * Customizable piece of the default paging text (defaults to "Page")
12458      * @type String
12459      */
12460     beforePageText : "Page",
12461     /**
12462      * Customizable piece of the default paging text (defaults to "of %0")
12463      * @type String
12464      */
12465     afterPageText : "of {0}",
12466     /**
12467      * Customizable piece of the default paging text (defaults to "First Page")
12468      * @type String
12469      */
12470     firstText : "First Page",
12471     /**
12472      * Customizable piece of the default paging text (defaults to "Previous Page")
12473      * @type String
12474      */
12475     prevText : "Previous Page",
12476     /**
12477      * Customizable piece of the default paging text (defaults to "Next Page")
12478      * @type String
12479      */
12480     nextText : "Next Page",
12481     /**
12482      * Customizable piece of the default paging text (defaults to "Last Page")
12483      * @type String
12484      */
12485     lastText : "Last Page",
12486     /**
12487      * Customizable piece of the default paging text (defaults to "Refresh")
12488      * @type String
12489      */
12490     refreshText : "Refresh",
12491
12492     // private
12493     renderButtons : function(el){
12494         Roo.PagingToolbar.superclass.render.call(this, el);
12495         this.first = this.addButton({
12496             tooltip: this.firstText,
12497             cls: "x-btn-icon x-grid-page-first",
12498             disabled: true,
12499             handler: this.onClick.createDelegate(this, ["first"])
12500         });
12501         this.prev = this.addButton({
12502             tooltip: this.prevText,
12503             cls: "x-btn-icon x-grid-page-prev",
12504             disabled: true,
12505             handler: this.onClick.createDelegate(this, ["prev"])
12506         });
12507         //this.addSeparator();
12508         this.add(this.beforePageText);
12509         this.field = Roo.get(this.addDom({
12510            tag: "input",
12511            type: "text",
12512            size: "3",
12513            value: "1",
12514            cls: "x-grid-page-number"
12515         }).el);
12516         this.field.on("keydown", this.onPagingKeydown, this);
12517         this.field.on("focus", function(){this.dom.select();});
12518         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12519         this.field.setHeight(18);
12520         //this.addSeparator();
12521         this.next = this.addButton({
12522             tooltip: this.nextText,
12523             cls: "x-btn-icon x-grid-page-next",
12524             disabled: true,
12525             handler: this.onClick.createDelegate(this, ["next"])
12526         });
12527         this.last = this.addButton({
12528             tooltip: this.lastText,
12529             cls: "x-btn-icon x-grid-page-last",
12530             disabled: true,
12531             handler: this.onClick.createDelegate(this, ["last"])
12532         });
12533         //this.addSeparator();
12534         this.loading = this.addButton({
12535             tooltip: this.refreshText,
12536             cls: "x-btn-icon x-grid-loading",
12537             handler: this.onClick.createDelegate(this, ["refresh"])
12538         });
12539
12540         if(this.displayInfo){
12541             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12542         }
12543     },
12544
12545     // private
12546     updateInfo : function(){
12547         if(this.displayEl){
12548             var count = this.ds.getCount();
12549             var msg = count == 0 ?
12550                 this.emptyMsg :
12551                 String.format(
12552                     this.displayMsg,
12553                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12554                 );
12555             this.displayEl.update(msg);
12556         }
12557     },
12558
12559     // private
12560     onLoad : function(ds, r, o){
12561        this.cursor = o.params ? o.params.start : 0;
12562        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12563
12564        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12565        this.field.dom.value = ap;
12566        this.first.setDisabled(ap == 1);
12567        this.prev.setDisabled(ap == 1);
12568        this.next.setDisabled(ap == ps);
12569        this.last.setDisabled(ap == ps);
12570        this.loading.enable();
12571        this.updateInfo();
12572     },
12573
12574     // private
12575     getPageData : function(){
12576         var total = this.ds.getTotalCount();
12577         return {
12578             total : total,
12579             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12580             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12581         };
12582     },
12583
12584     // private
12585     onLoadError : function(){
12586         this.loading.enable();
12587     },
12588
12589     // private
12590     onPagingKeydown : function(e){
12591         var k = e.getKey();
12592         var d = this.getPageData();
12593         if(k == e.RETURN){
12594             var v = this.field.dom.value, pageNum;
12595             if(!v || isNaN(pageNum = parseInt(v, 10))){
12596                 this.field.dom.value = d.activePage;
12597                 return;
12598             }
12599             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12600             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12601             e.stopEvent();
12602         }
12603         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))
12604         {
12605           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12606           this.field.dom.value = pageNum;
12607           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12608           e.stopEvent();
12609         }
12610         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12611         {
12612           var v = this.field.dom.value, pageNum; 
12613           var increment = (e.shiftKey) ? 10 : 1;
12614           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12615             increment *= -1;
12616           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12617             this.field.dom.value = d.activePage;
12618             return;
12619           }
12620           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12621           {
12622             this.field.dom.value = parseInt(v, 10) + increment;
12623             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12624             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12625           }
12626           e.stopEvent();
12627         }
12628     },
12629
12630     // private
12631     beforeLoad : function(){
12632         if(this.loading){
12633             this.loading.disable();
12634         }
12635     },
12636
12637     // private
12638     onClick : function(which){
12639         var ds = this.ds;
12640         switch(which){
12641             case "first":
12642                 ds.load({params:{start: 0, limit: this.pageSize}});
12643             break;
12644             case "prev":
12645                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12646             break;
12647             case "next":
12648                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12649             break;
12650             case "last":
12651                 var total = ds.getTotalCount();
12652                 var extra = total % this.pageSize;
12653                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12654                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12655             break;
12656             case "refresh":
12657                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12658             break;
12659         }
12660     },
12661
12662     /**
12663      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12664      * @param {Roo.data.Store} store The data store to unbind
12665      */
12666     unbind : function(ds){
12667         ds.un("beforeload", this.beforeLoad, this);
12668         ds.un("load", this.onLoad, this);
12669         ds.un("loadexception", this.onLoadError, this);
12670         ds.un("remove", this.updateInfo, this);
12671         ds.un("add", this.updateInfo, this);
12672         this.ds = undefined;
12673     },
12674
12675     /**
12676      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12677      * @param {Roo.data.Store} store The data store to bind
12678      */
12679     bind : function(ds){
12680         ds.on("beforeload", this.beforeLoad, this);
12681         ds.on("load", this.onLoad, this);
12682         ds.on("loadexception", this.onLoadError, this);
12683         ds.on("remove", this.updateInfo, this);
12684         ds.on("add", this.updateInfo, this);
12685         this.ds = ds;
12686     }
12687 });/*
12688  * Based on:
12689  * Ext JS Library 1.1.1
12690  * Copyright(c) 2006-2007, Ext JS, LLC.
12691  *
12692  * Originally Released Under LGPL - original licence link has changed is not relivant.
12693  *
12694  * Fork - LGPL
12695  * <script type="text/javascript">
12696  */
12697
12698 /**
12699  * @class Roo.Resizable
12700  * @extends Roo.util.Observable
12701  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12702  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12703  * 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
12704  * the element will be wrapped for you automatically.</p>
12705  * <p>Here is the list of valid resize handles:</p>
12706  * <pre>
12707 Value   Description
12708 ------  -------------------
12709  'n'     north
12710  's'     south
12711  'e'     east
12712  'w'     west
12713  'nw'    northwest
12714  'sw'    southwest
12715  'se'    southeast
12716  'ne'    northeast
12717  'hd'    horizontal drag
12718  'all'   all
12719 </pre>
12720  * <p>Here's an example showing the creation of a typical Resizable:</p>
12721  * <pre><code>
12722 var resizer = new Roo.Resizable("element-id", {
12723     handles: 'all',
12724     minWidth: 200,
12725     minHeight: 100,
12726     maxWidth: 500,
12727     maxHeight: 400,
12728     pinned: true
12729 });
12730 resizer.on("resize", myHandler);
12731 </code></pre>
12732  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12733  * resizer.east.setDisplayed(false);</p>
12734  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12735  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12736  * resize operation's new size (defaults to [0, 0])
12737  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12738  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12739  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12740  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12741  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12742  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12743  * @cfg {Number} width The width of the element in pixels (defaults to null)
12744  * @cfg {Number} height The height of the element in pixels (defaults to null)
12745  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12746  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12747  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12748  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12749  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12750  * in favor of the handles config option (defaults to false)
12751  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12752  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12753  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12754  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12755  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12756  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12757  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12758  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12759  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12760  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12761  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12762  * @constructor
12763  * Create a new resizable component
12764  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12765  * @param {Object} config configuration options
12766   */
12767 Roo.Resizable = function(el, config)
12768 {
12769     this.el = Roo.get(el);
12770
12771     if(config && config.wrap){
12772         config.resizeChild = this.el;
12773         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12774         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12775         this.el.setStyle("overflow", "hidden");
12776         this.el.setPositioning(config.resizeChild.getPositioning());
12777         config.resizeChild.clearPositioning();
12778         if(!config.width || !config.height){
12779             var csize = config.resizeChild.getSize();
12780             this.el.setSize(csize.width, csize.height);
12781         }
12782         if(config.pinned && !config.adjustments){
12783             config.adjustments = "auto";
12784         }
12785     }
12786
12787     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12788     this.proxy.unselectable();
12789     this.proxy.enableDisplayMode('block');
12790
12791     Roo.apply(this, config);
12792
12793     if(this.pinned){
12794         this.disableTrackOver = true;
12795         this.el.addClass("x-resizable-pinned");
12796     }
12797     // if the element isn't positioned, make it relative
12798     var position = this.el.getStyle("position");
12799     if(position != "absolute" && position != "fixed"){
12800         this.el.setStyle("position", "relative");
12801     }
12802     if(!this.handles){ // no handles passed, must be legacy style
12803         this.handles = 's,e,se';
12804         if(this.multiDirectional){
12805             this.handles += ',n,w';
12806         }
12807     }
12808     if(this.handles == "all"){
12809         this.handles = "n s e w ne nw se sw";
12810     }
12811     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12812     var ps = Roo.Resizable.positions;
12813     for(var i = 0, len = hs.length; i < len; i++){
12814         if(hs[i] && ps[hs[i]]){
12815             var pos = ps[hs[i]];
12816             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12817         }
12818     }
12819     // legacy
12820     this.corner = this.southeast;
12821     
12822     // updateBox = the box can move..
12823     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12824         this.updateBox = true;
12825     }
12826
12827     this.activeHandle = null;
12828
12829     if(this.resizeChild){
12830         if(typeof this.resizeChild == "boolean"){
12831             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12832         }else{
12833             this.resizeChild = Roo.get(this.resizeChild, true);
12834         }
12835     }
12836     
12837     if(this.adjustments == "auto"){
12838         var rc = this.resizeChild;
12839         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12840         if(rc && (hw || hn)){
12841             rc.position("relative");
12842             rc.setLeft(hw ? hw.el.getWidth() : 0);
12843             rc.setTop(hn ? hn.el.getHeight() : 0);
12844         }
12845         this.adjustments = [
12846             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12847             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12848         ];
12849     }
12850
12851     if(this.draggable){
12852         this.dd = this.dynamic ?
12853             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12854         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12855     }
12856
12857     // public events
12858     this.addEvents({
12859         /**
12860          * @event beforeresize
12861          * Fired before resize is allowed. Set enabled to false to cancel resize.
12862          * @param {Roo.Resizable} this
12863          * @param {Roo.EventObject} e The mousedown event
12864          */
12865         "beforeresize" : true,
12866         /**
12867          * @event resizing
12868          * Fired a resizing.
12869          * @param {Roo.Resizable} this
12870          * @param {Number} x The new x position
12871          * @param {Number} y The new y position
12872          * @param {Number} w The new w width
12873          * @param {Number} h The new h hight
12874          * @param {Roo.EventObject} e The mouseup event
12875          */
12876         "resizing" : true,
12877         /**
12878          * @event resize
12879          * Fired after a resize.
12880          * @param {Roo.Resizable} this
12881          * @param {Number} width The new width
12882          * @param {Number} height The new height
12883          * @param {Roo.EventObject} e The mouseup event
12884          */
12885         "resize" : true
12886     });
12887
12888     if(this.width !== null && this.height !== null){
12889         this.resizeTo(this.width, this.height);
12890     }else{
12891         this.updateChildSize();
12892     }
12893     if(Roo.isIE){
12894         this.el.dom.style.zoom = 1;
12895     }
12896     Roo.Resizable.superclass.constructor.call(this);
12897 };
12898
12899 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12900         resizeChild : false,
12901         adjustments : [0, 0],
12902         minWidth : 5,
12903         minHeight : 5,
12904         maxWidth : 10000,
12905         maxHeight : 10000,
12906         enabled : true,
12907         animate : false,
12908         duration : .35,
12909         dynamic : false,
12910         handles : false,
12911         multiDirectional : false,
12912         disableTrackOver : false,
12913         easing : 'easeOutStrong',
12914         widthIncrement : 0,
12915         heightIncrement : 0,
12916         pinned : false,
12917         width : null,
12918         height : null,
12919         preserveRatio : false,
12920         transparent: false,
12921         minX: 0,
12922         minY: 0,
12923         draggable: false,
12924
12925         /**
12926          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12927          */
12928         constrainTo: undefined,
12929         /**
12930          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12931          */
12932         resizeRegion: undefined,
12933
12934
12935     /**
12936      * Perform a manual resize
12937      * @param {Number} width
12938      * @param {Number} height
12939      */
12940     resizeTo : function(width, height){
12941         this.el.setSize(width, height);
12942         this.updateChildSize();
12943         this.fireEvent("resize", this, width, height, null);
12944     },
12945
12946     // private
12947     startSizing : function(e, handle){
12948         this.fireEvent("beforeresize", this, e);
12949         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12950
12951             if(!this.overlay){
12952                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12953                 this.overlay.unselectable();
12954                 this.overlay.enableDisplayMode("block");
12955                 this.overlay.on("mousemove", this.onMouseMove, this);
12956                 this.overlay.on("mouseup", this.onMouseUp, this);
12957             }
12958             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12959
12960             this.resizing = true;
12961             this.startBox = this.el.getBox();
12962             this.startPoint = e.getXY();
12963             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12964                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12965
12966             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12967             this.overlay.show();
12968
12969             if(this.constrainTo) {
12970                 var ct = Roo.get(this.constrainTo);
12971                 this.resizeRegion = ct.getRegion().adjust(
12972                     ct.getFrameWidth('t'),
12973                     ct.getFrameWidth('l'),
12974                     -ct.getFrameWidth('b'),
12975                     -ct.getFrameWidth('r')
12976                 );
12977             }
12978
12979             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12980             this.proxy.show();
12981             this.proxy.setBox(this.startBox);
12982             if(!this.dynamic){
12983                 this.proxy.setStyle('visibility', 'visible');
12984             }
12985         }
12986     },
12987
12988     // private
12989     onMouseDown : function(handle, e){
12990         if(this.enabled){
12991             e.stopEvent();
12992             this.activeHandle = handle;
12993             this.startSizing(e, handle);
12994         }
12995     },
12996
12997     // private
12998     onMouseUp : function(e){
12999         var size = this.resizeElement();
13000         this.resizing = false;
13001         this.handleOut();
13002         this.overlay.hide();
13003         this.proxy.hide();
13004         this.fireEvent("resize", this, size.width, size.height, e);
13005     },
13006
13007     // private
13008     updateChildSize : function(){
13009         
13010         if(this.resizeChild){
13011             var el = this.el;
13012             var child = this.resizeChild;
13013             var adj = this.adjustments;
13014             if(el.dom.offsetWidth){
13015                 var b = el.getSize(true);
13016                 child.setSize(b.width+adj[0], b.height+adj[1]);
13017             }
13018             // Second call here for IE
13019             // The first call enables instant resizing and
13020             // the second call corrects scroll bars if they
13021             // exist
13022             if(Roo.isIE){
13023                 setTimeout(function(){
13024                     if(el.dom.offsetWidth){
13025                         var b = el.getSize(true);
13026                         child.setSize(b.width+adj[0], b.height+adj[1]);
13027                     }
13028                 }, 10);
13029             }
13030         }
13031     },
13032
13033     // private
13034     snap : function(value, inc, min){
13035         if(!inc || !value) return value;
13036         var newValue = value;
13037         var m = value % inc;
13038         if(m > 0){
13039             if(m > (inc/2)){
13040                 newValue = value + (inc-m);
13041             }else{
13042                 newValue = value - m;
13043             }
13044         }
13045         return Math.max(min, newValue);
13046     },
13047
13048     // private
13049     resizeElement : function(){
13050         var box = this.proxy.getBox();
13051         if(this.updateBox){
13052             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13053         }else{
13054             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13055         }
13056         this.updateChildSize();
13057         if(!this.dynamic){
13058             this.proxy.hide();
13059         }
13060         return box;
13061     },
13062
13063     // private
13064     constrain : function(v, diff, m, mx){
13065         if(v - diff < m){
13066             diff = v - m;
13067         }else if(v - diff > mx){
13068             diff = mx - v;
13069         }
13070         return diff;
13071     },
13072
13073     // private
13074     onMouseMove : function(e){
13075         
13076         if(this.enabled){
13077             try{// try catch so if something goes wrong the user doesn't get hung
13078
13079             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13080                 return;
13081             }
13082
13083             //var curXY = this.startPoint;
13084             var curSize = this.curSize || this.startBox;
13085             var x = this.startBox.x, y = this.startBox.y;
13086             var ox = x, oy = y;
13087             var w = curSize.width, h = curSize.height;
13088             var ow = w, oh = h;
13089             var mw = this.minWidth, mh = this.minHeight;
13090             var mxw = this.maxWidth, mxh = this.maxHeight;
13091             var wi = this.widthIncrement;
13092             var hi = this.heightIncrement;
13093
13094             var eventXY = e.getXY();
13095             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13096             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13097
13098             var pos = this.activeHandle.position;
13099
13100             switch(pos){
13101                 case "east":
13102                     w += diffX;
13103                     w = Math.min(Math.max(mw, w), mxw);
13104                     break;
13105              
13106                 case "south":
13107                     h += diffY;
13108                     h = Math.min(Math.max(mh, h), mxh);
13109                     break;
13110                 case "southeast":
13111                     w += diffX;
13112                     h += diffY;
13113                     w = Math.min(Math.max(mw, w), mxw);
13114                     h = Math.min(Math.max(mh, h), mxh);
13115                     break;
13116                 case "north":
13117                     diffY = this.constrain(h, diffY, mh, mxh);
13118                     y += diffY;
13119                     h -= diffY;
13120                     break;
13121                 case "hdrag":
13122                     
13123                     if (wi) {
13124                         var adiffX = Math.abs(diffX);
13125                         var sub = (adiffX % wi); // how much 
13126                         if (sub > (wi/2)) { // far enough to snap
13127                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13128                         } else {
13129                             // remove difference.. 
13130                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13131                         }
13132                     }
13133                     x += diffX;
13134                     x = Math.max(this.minX, x);
13135                     break;
13136                 case "west":
13137                     diffX = this.constrain(w, diffX, mw, mxw);
13138                     x += diffX;
13139                     w -= diffX;
13140                     break;
13141                 case "northeast":
13142                     w += diffX;
13143                     w = Math.min(Math.max(mw, w), mxw);
13144                     diffY = this.constrain(h, diffY, mh, mxh);
13145                     y += diffY;
13146                     h -= diffY;
13147                     break;
13148                 case "northwest":
13149                     diffX = this.constrain(w, diffX, mw, mxw);
13150                     diffY = this.constrain(h, diffY, mh, mxh);
13151                     y += diffY;
13152                     h -= diffY;
13153                     x += diffX;
13154                     w -= diffX;
13155                     break;
13156                case "southwest":
13157                     diffX = this.constrain(w, diffX, mw, mxw);
13158                     h += diffY;
13159                     h = Math.min(Math.max(mh, h), mxh);
13160                     x += diffX;
13161                     w -= diffX;
13162                     break;
13163             }
13164
13165             var sw = this.snap(w, wi, mw);
13166             var sh = this.snap(h, hi, mh);
13167             if(sw != w || sh != h){
13168                 switch(pos){
13169                     case "northeast":
13170                         y -= sh - h;
13171                     break;
13172                     case "north":
13173                         y -= sh - h;
13174                         break;
13175                     case "southwest":
13176                         x -= sw - w;
13177                     break;
13178                     case "west":
13179                         x -= sw - w;
13180                         break;
13181                     case "northwest":
13182                         x -= sw - w;
13183                         y -= sh - h;
13184                     break;
13185                 }
13186                 w = sw;
13187                 h = sh;
13188             }
13189
13190             if(this.preserveRatio){
13191                 switch(pos){
13192                     case "southeast":
13193                     case "east":
13194                         h = oh * (w/ow);
13195                         h = Math.min(Math.max(mh, h), mxh);
13196                         w = ow * (h/oh);
13197                        break;
13198                     case "south":
13199                         w = ow * (h/oh);
13200                         w = Math.min(Math.max(mw, w), mxw);
13201                         h = oh * (w/ow);
13202                         break;
13203                     case "northeast":
13204                         w = ow * (h/oh);
13205                         w = Math.min(Math.max(mw, w), mxw);
13206                         h = oh * (w/ow);
13207                     break;
13208                     case "north":
13209                         var tw = w;
13210                         w = ow * (h/oh);
13211                         w = Math.min(Math.max(mw, w), mxw);
13212                         h = oh * (w/ow);
13213                         x += (tw - w) / 2;
13214                         break;
13215                     case "southwest":
13216                         h = oh * (w/ow);
13217                         h = Math.min(Math.max(mh, h), mxh);
13218                         var tw = w;
13219                         w = ow * (h/oh);
13220                         x += tw - w;
13221                         break;
13222                     case "west":
13223                         var th = h;
13224                         h = oh * (w/ow);
13225                         h = Math.min(Math.max(mh, h), mxh);
13226                         y += (th - h) / 2;
13227                         var tw = w;
13228                         w = ow * (h/oh);
13229                         x += tw - w;
13230                        break;
13231                     case "northwest":
13232                         var tw = w;
13233                         var th = h;
13234                         h = oh * (w/ow);
13235                         h = Math.min(Math.max(mh, h), mxh);
13236                         w = ow * (h/oh);
13237                         y += th - h;
13238                         x += tw - w;
13239                        break;
13240
13241                 }
13242             }
13243             if (pos == 'hdrag') {
13244                 w = ow;
13245             }
13246             this.proxy.setBounds(x, y, w, h);
13247             if(this.dynamic){
13248                 this.resizeElement();
13249             }
13250             }catch(e){}
13251         }
13252         this.fireEvent("resizing", this, x, y, w, h, e);
13253     },
13254
13255     // private
13256     handleOver : function(){
13257         if(this.enabled){
13258             this.el.addClass("x-resizable-over");
13259         }
13260     },
13261
13262     // private
13263     handleOut : function(){
13264         if(!this.resizing){
13265             this.el.removeClass("x-resizable-over");
13266         }
13267     },
13268
13269     /**
13270      * Returns the element this component is bound to.
13271      * @return {Roo.Element}
13272      */
13273     getEl : function(){
13274         return this.el;
13275     },
13276
13277     /**
13278      * Returns the resizeChild element (or null).
13279      * @return {Roo.Element}
13280      */
13281     getResizeChild : function(){
13282         return this.resizeChild;
13283     },
13284     groupHandler : function()
13285     {
13286         
13287     },
13288     /**
13289      * Destroys this resizable. If the element was wrapped and
13290      * removeEl is not true then the element remains.
13291      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13292      */
13293     destroy : function(removeEl){
13294         this.proxy.remove();
13295         if(this.overlay){
13296             this.overlay.removeAllListeners();
13297             this.overlay.remove();
13298         }
13299         var ps = Roo.Resizable.positions;
13300         for(var k in ps){
13301             if(typeof ps[k] != "function" && this[ps[k]]){
13302                 var h = this[ps[k]];
13303                 h.el.removeAllListeners();
13304                 h.el.remove();
13305             }
13306         }
13307         if(removeEl){
13308             this.el.update("");
13309             this.el.remove();
13310         }
13311     }
13312 });
13313
13314 // private
13315 // hash to map config positions to true positions
13316 Roo.Resizable.positions = {
13317     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13318     hd: "hdrag"
13319 };
13320
13321 // private
13322 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13323     if(!this.tpl){
13324         // only initialize the template if resizable is used
13325         var tpl = Roo.DomHelper.createTemplate(
13326             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13327         );
13328         tpl.compile();
13329         Roo.Resizable.Handle.prototype.tpl = tpl;
13330     }
13331     this.position = pos;
13332     this.rz = rz;
13333     // show north drag fro topdra
13334     var handlepos = pos == 'hdrag' ? 'north' : pos;
13335     
13336     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13337     if (pos == 'hdrag') {
13338         this.el.setStyle('cursor', 'pointer');
13339     }
13340     this.el.unselectable();
13341     if(transparent){
13342         this.el.setOpacity(0);
13343     }
13344     this.el.on("mousedown", this.onMouseDown, this);
13345     if(!disableTrackOver){
13346         this.el.on("mouseover", this.onMouseOver, this);
13347         this.el.on("mouseout", this.onMouseOut, this);
13348     }
13349 };
13350
13351 // private
13352 Roo.Resizable.Handle.prototype = {
13353     afterResize : function(rz){
13354         Roo.log('after?');
13355         // do nothing
13356     },
13357     // private
13358     onMouseDown : function(e){
13359         this.rz.onMouseDown(this, e);
13360     },
13361     // private
13362     onMouseOver : function(e){
13363         this.rz.handleOver(this, e);
13364     },
13365     // private
13366     onMouseOut : function(e){
13367         this.rz.handleOut(this, e);
13368     }
13369 };/*
13370  * Based on:
13371  * Ext JS Library 1.1.1
13372  * Copyright(c) 2006-2007, Ext JS, LLC.
13373  *
13374  * Originally Released Under LGPL - original licence link has changed is not relivant.
13375  *
13376  * Fork - LGPL
13377  * <script type="text/javascript">
13378  */
13379
13380 /**
13381  * @class Roo.Editor
13382  * @extends Roo.Component
13383  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13384  * @constructor
13385  * Create a new Editor
13386  * @param {Roo.form.Field} field The Field object (or descendant)
13387  * @param {Object} config The config object
13388  */
13389 Roo.Editor = function(field, config){
13390     Roo.Editor.superclass.constructor.call(this, config);
13391     this.field = field;
13392     this.addEvents({
13393         /**
13394              * @event beforestartedit
13395              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13396              * false from the handler of this event.
13397              * @param {Editor} this
13398              * @param {Roo.Element} boundEl The underlying element bound to this editor
13399              * @param {Mixed} value The field value being set
13400              */
13401         "beforestartedit" : true,
13402         /**
13403              * @event startedit
13404              * Fires when this editor is displayed
13405              * @param {Roo.Element} boundEl The underlying element bound to this editor
13406              * @param {Mixed} value The starting field value
13407              */
13408         "startedit" : true,
13409         /**
13410              * @event beforecomplete
13411              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13412              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13413              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13414              * event will not fire since no edit actually occurred.
13415              * @param {Editor} this
13416              * @param {Mixed} value The current field value
13417              * @param {Mixed} startValue The original field value
13418              */
13419         "beforecomplete" : true,
13420         /**
13421              * @event complete
13422              * Fires after editing is complete and any changed value has been written to the underlying field.
13423              * @param {Editor} this
13424              * @param {Mixed} value The current field value
13425              * @param {Mixed} startValue The original field value
13426              */
13427         "complete" : true,
13428         /**
13429          * @event specialkey
13430          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13431          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13432          * @param {Roo.form.Field} this
13433          * @param {Roo.EventObject} e The event object
13434          */
13435         "specialkey" : true
13436     });
13437 };
13438
13439 Roo.extend(Roo.Editor, Roo.Component, {
13440     /**
13441      * @cfg {Boolean/String} autosize
13442      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13443      * or "height" to adopt the height only (defaults to false)
13444      */
13445     /**
13446      * @cfg {Boolean} revertInvalid
13447      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13448      * validation fails (defaults to true)
13449      */
13450     /**
13451      * @cfg {Boolean} ignoreNoChange
13452      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13453      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13454      * will never be ignored.
13455      */
13456     /**
13457      * @cfg {Boolean} hideEl
13458      * False to keep the bound element visible while the editor is displayed (defaults to true)
13459      */
13460     /**
13461      * @cfg {Mixed} value
13462      * The data value of the underlying field (defaults to "")
13463      */
13464     value : "",
13465     /**
13466      * @cfg {String} alignment
13467      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13468      */
13469     alignment: "c-c?",
13470     /**
13471      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13472      * for bottom-right shadow (defaults to "frame")
13473      */
13474     shadow : "frame",
13475     /**
13476      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13477      */
13478     constrain : false,
13479     /**
13480      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13481      */
13482     completeOnEnter : false,
13483     /**
13484      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13485      */
13486     cancelOnEsc : false,
13487     /**
13488      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13489      */
13490     updateEl : false,
13491
13492     // private
13493     onRender : function(ct, position){
13494         this.el = new Roo.Layer({
13495             shadow: this.shadow,
13496             cls: "x-editor",
13497             parentEl : ct,
13498             shim : this.shim,
13499             shadowOffset:4,
13500             id: this.id,
13501             constrain: this.constrain
13502         });
13503         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13504         if(this.field.msgTarget != 'title'){
13505             this.field.msgTarget = 'qtip';
13506         }
13507         this.field.render(this.el);
13508         if(Roo.isGecko){
13509             this.field.el.dom.setAttribute('autocomplete', 'off');
13510         }
13511         this.field.on("specialkey", this.onSpecialKey, this);
13512         if(this.swallowKeys){
13513             this.field.el.swallowEvent(['keydown','keypress']);
13514         }
13515         this.field.show();
13516         this.field.on("blur", this.onBlur, this);
13517         if(this.field.grow){
13518             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13519         }
13520     },
13521
13522     onSpecialKey : function(field, e)
13523     {
13524         //Roo.log('editor onSpecialKey');
13525         if(this.completeOnEnter && e.getKey() == e.ENTER){
13526             e.stopEvent();
13527             this.completeEdit();
13528             return;
13529         }
13530         // do not fire special key otherwise it might hide close the editor...
13531         if(e.getKey() == e.ENTER){    
13532             return;
13533         }
13534         if(this.cancelOnEsc && e.getKey() == e.ESC){
13535             this.cancelEdit();
13536             return;
13537         } 
13538         this.fireEvent('specialkey', field, e);
13539     
13540     },
13541
13542     /**
13543      * Starts the editing process and shows the editor.
13544      * @param {String/HTMLElement/Element} el The element to edit
13545      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13546       * to the innerHTML of el.
13547      */
13548     startEdit : function(el, value){
13549         if(this.editing){
13550             this.completeEdit();
13551         }
13552         this.boundEl = Roo.get(el);
13553         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13554         if(!this.rendered){
13555             this.render(this.parentEl || document.body);
13556         }
13557         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13558             return;
13559         }
13560         this.startValue = v;
13561         this.field.setValue(v);
13562         if(this.autoSize){
13563             var sz = this.boundEl.getSize();
13564             switch(this.autoSize){
13565                 case "width":
13566                 this.setSize(sz.width,  "");
13567                 break;
13568                 case "height":
13569                 this.setSize("",  sz.height);
13570                 break;
13571                 default:
13572                 this.setSize(sz.width,  sz.height);
13573             }
13574         }
13575         this.el.alignTo(this.boundEl, this.alignment);
13576         this.editing = true;
13577         if(Roo.QuickTips){
13578             Roo.QuickTips.disable();
13579         }
13580         this.show();
13581     },
13582
13583     /**
13584      * Sets the height and width of this editor.
13585      * @param {Number} width The new width
13586      * @param {Number} height The new height
13587      */
13588     setSize : function(w, h){
13589         this.field.setSize(w, h);
13590         if(this.el){
13591             this.el.sync();
13592         }
13593     },
13594
13595     /**
13596      * Realigns the editor to the bound field based on the current alignment config value.
13597      */
13598     realign : function(){
13599         this.el.alignTo(this.boundEl, this.alignment);
13600     },
13601
13602     /**
13603      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13604      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13605      */
13606     completeEdit : function(remainVisible){
13607         if(!this.editing){
13608             return;
13609         }
13610         var v = this.getValue();
13611         if(this.revertInvalid !== false && !this.field.isValid()){
13612             v = this.startValue;
13613             this.cancelEdit(true);
13614         }
13615         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13616             this.editing = false;
13617             this.hide();
13618             return;
13619         }
13620         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13621             this.editing = false;
13622             if(this.updateEl && this.boundEl){
13623                 this.boundEl.update(v);
13624             }
13625             if(remainVisible !== true){
13626                 this.hide();
13627             }
13628             this.fireEvent("complete", this, v, this.startValue);
13629         }
13630     },
13631
13632     // private
13633     onShow : function(){
13634         this.el.show();
13635         if(this.hideEl !== false){
13636             this.boundEl.hide();
13637         }
13638         this.field.show();
13639         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13640             this.fixIEFocus = true;
13641             this.deferredFocus.defer(50, this);
13642         }else{
13643             this.field.focus();
13644         }
13645         this.fireEvent("startedit", this.boundEl, this.startValue);
13646     },
13647
13648     deferredFocus : function(){
13649         if(this.editing){
13650             this.field.focus();
13651         }
13652     },
13653
13654     /**
13655      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13656      * reverted to the original starting value.
13657      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13658      * cancel (defaults to false)
13659      */
13660     cancelEdit : function(remainVisible){
13661         if(this.editing){
13662             this.setValue(this.startValue);
13663             if(remainVisible !== true){
13664                 this.hide();
13665             }
13666         }
13667     },
13668
13669     // private
13670     onBlur : function(){
13671         if(this.allowBlur !== true && this.editing){
13672             this.completeEdit();
13673         }
13674     },
13675
13676     // private
13677     onHide : function(){
13678         if(this.editing){
13679             this.completeEdit();
13680             return;
13681         }
13682         this.field.blur();
13683         if(this.field.collapse){
13684             this.field.collapse();
13685         }
13686         this.el.hide();
13687         if(this.hideEl !== false){
13688             this.boundEl.show();
13689         }
13690         if(Roo.QuickTips){
13691             Roo.QuickTips.enable();
13692         }
13693     },
13694
13695     /**
13696      * Sets the data value of the editor
13697      * @param {Mixed} value Any valid value supported by the underlying field
13698      */
13699     setValue : function(v){
13700         this.field.setValue(v);
13701     },
13702
13703     /**
13704      * Gets the data value of the editor
13705      * @return {Mixed} The data value
13706      */
13707     getValue : function(){
13708         return this.field.getValue();
13709     }
13710 });/*
13711  * Based on:
13712  * Ext JS Library 1.1.1
13713  * Copyright(c) 2006-2007, Ext JS, LLC.
13714  *
13715  * Originally Released Under LGPL - original licence link has changed is not relivant.
13716  *
13717  * Fork - LGPL
13718  * <script type="text/javascript">
13719  */
13720  
13721 /**
13722  * @class Roo.BasicDialog
13723  * @extends Roo.util.Observable
13724  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13725  * <pre><code>
13726 var dlg = new Roo.BasicDialog("my-dlg", {
13727     height: 200,
13728     width: 300,
13729     minHeight: 100,
13730     minWidth: 150,
13731     modal: true,
13732     proxyDrag: true,
13733     shadow: true
13734 });
13735 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13736 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13737 dlg.addButton('Cancel', dlg.hide, dlg);
13738 dlg.show();
13739 </code></pre>
13740   <b>A Dialog should always be a direct child of the body element.</b>
13741  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13742  * @cfg {String} title Default text to display in the title bar (defaults to null)
13743  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13744  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13745  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13746  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13747  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13748  * (defaults to null with no animation)
13749  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13750  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13751  * property for valid values (defaults to 'all')
13752  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13753  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13754  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13755  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13756  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13757  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13758  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13759  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13760  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13761  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13762  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13763  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13764  * draggable = true (defaults to false)
13765  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13766  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13767  * shadow (defaults to false)
13768  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13769  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13770  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13771  * @cfg {Array} buttons Array of buttons
13772  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13773  * @constructor
13774  * Create a new BasicDialog.
13775  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13776  * @param {Object} config Configuration options
13777  */
13778 Roo.BasicDialog = function(el, config){
13779     this.el = Roo.get(el);
13780     var dh = Roo.DomHelper;
13781     if(!this.el && config && config.autoCreate){
13782         if(typeof config.autoCreate == "object"){
13783             if(!config.autoCreate.id){
13784                 config.autoCreate.id = el;
13785             }
13786             this.el = dh.append(document.body,
13787                         config.autoCreate, true);
13788         }else{
13789             this.el = dh.append(document.body,
13790                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13791         }
13792     }
13793     el = this.el;
13794     el.setDisplayed(true);
13795     el.hide = this.hideAction;
13796     this.id = el.id;
13797     el.addClass("x-dlg");
13798
13799     Roo.apply(this, config);
13800
13801     this.proxy = el.createProxy("x-dlg-proxy");
13802     this.proxy.hide = this.hideAction;
13803     this.proxy.setOpacity(.5);
13804     this.proxy.hide();
13805
13806     if(config.width){
13807         el.setWidth(config.width);
13808     }
13809     if(config.height){
13810         el.setHeight(config.height);
13811     }
13812     this.size = el.getSize();
13813     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13814         this.xy = [config.x,config.y];
13815     }else{
13816         this.xy = el.getCenterXY(true);
13817     }
13818     /** The header element @type Roo.Element */
13819     this.header = el.child("> .x-dlg-hd");
13820     /** The body element @type Roo.Element */
13821     this.body = el.child("> .x-dlg-bd");
13822     /** The footer element @type Roo.Element */
13823     this.footer = el.child("> .x-dlg-ft");
13824
13825     if(!this.header){
13826         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13827     }
13828     if(!this.body){
13829         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13830     }
13831
13832     this.header.unselectable();
13833     if(this.title){
13834         this.header.update(this.title);
13835     }
13836     // this element allows the dialog to be focused for keyboard event
13837     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13838     this.focusEl.swallowEvent("click", true);
13839
13840     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13841
13842     // wrap the body and footer for special rendering
13843     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13844     if(this.footer){
13845         this.bwrap.dom.appendChild(this.footer.dom);
13846     }
13847
13848     this.bg = this.el.createChild({
13849         tag: "div", cls:"x-dlg-bg",
13850         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13851     });
13852     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13853
13854
13855     if(this.autoScroll !== false && !this.autoTabs){
13856         this.body.setStyle("overflow", "auto");
13857     }
13858
13859     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13860
13861     if(this.closable !== false){
13862         this.el.addClass("x-dlg-closable");
13863         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13864         this.close.on("click", this.closeClick, this);
13865         this.close.addClassOnOver("x-dlg-close-over");
13866     }
13867     if(this.collapsible !== false){
13868         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13869         this.collapseBtn.on("click", this.collapseClick, this);
13870         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13871         this.header.on("dblclick", this.collapseClick, this);
13872     }
13873     if(this.resizable !== false){
13874         this.el.addClass("x-dlg-resizable");
13875         this.resizer = new Roo.Resizable(el, {
13876             minWidth: this.minWidth || 80,
13877             minHeight:this.minHeight || 80,
13878             handles: this.resizeHandles || "all",
13879             pinned: true
13880         });
13881         this.resizer.on("beforeresize", this.beforeResize, this);
13882         this.resizer.on("resize", this.onResize, this);
13883     }
13884     if(this.draggable !== false){
13885         el.addClass("x-dlg-draggable");
13886         if (!this.proxyDrag) {
13887             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13888         }
13889         else {
13890             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13891         }
13892         dd.setHandleElId(this.header.id);
13893         dd.endDrag = this.endMove.createDelegate(this);
13894         dd.startDrag = this.startMove.createDelegate(this);
13895         dd.onDrag = this.onDrag.createDelegate(this);
13896         dd.scroll = false;
13897         this.dd = dd;
13898     }
13899     if(this.modal){
13900         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13901         this.mask.enableDisplayMode("block");
13902         this.mask.hide();
13903         this.el.addClass("x-dlg-modal");
13904     }
13905     if(this.shadow){
13906         this.shadow = new Roo.Shadow({
13907             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13908             offset : this.shadowOffset
13909         });
13910     }else{
13911         this.shadowOffset = 0;
13912     }
13913     if(Roo.useShims && this.shim !== false){
13914         this.shim = this.el.createShim();
13915         this.shim.hide = this.hideAction;
13916         this.shim.hide();
13917     }else{
13918         this.shim = false;
13919     }
13920     if(this.autoTabs){
13921         this.initTabs();
13922     }
13923     if (this.buttons) { 
13924         var bts= this.buttons;
13925         this.buttons = [];
13926         Roo.each(bts, function(b) {
13927             this.addButton(b);
13928         }, this);
13929     }
13930     
13931     
13932     this.addEvents({
13933         /**
13934          * @event keydown
13935          * Fires when a key is pressed
13936          * @param {Roo.BasicDialog} this
13937          * @param {Roo.EventObject} e
13938          */
13939         "keydown" : true,
13940         /**
13941          * @event move
13942          * Fires when this dialog is moved by the user.
13943          * @param {Roo.BasicDialog} this
13944          * @param {Number} x The new page X
13945          * @param {Number} y The new page Y
13946          */
13947         "move" : true,
13948         /**
13949          * @event resize
13950          * Fires when this dialog is resized by the user.
13951          * @param {Roo.BasicDialog} this
13952          * @param {Number} width The new width
13953          * @param {Number} height The new height
13954          */
13955         "resize" : true,
13956         /**
13957          * @event beforehide
13958          * Fires before this dialog is hidden.
13959          * @param {Roo.BasicDialog} this
13960          */
13961         "beforehide" : true,
13962         /**
13963          * @event hide
13964          * Fires when this dialog is hidden.
13965          * @param {Roo.BasicDialog} this
13966          */
13967         "hide" : true,
13968         /**
13969          * @event beforeshow
13970          * Fires before this dialog is shown.
13971          * @param {Roo.BasicDialog} this
13972          */
13973         "beforeshow" : true,
13974         /**
13975          * @event show
13976          * Fires when this dialog is shown.
13977          * @param {Roo.BasicDialog} this
13978          */
13979         "show" : true
13980     });
13981     el.on("keydown", this.onKeyDown, this);
13982     el.on("mousedown", this.toFront, this);
13983     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13984     this.el.hide();
13985     Roo.DialogManager.register(this);
13986     Roo.BasicDialog.superclass.constructor.call(this);
13987 };
13988
13989 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13990     shadowOffset: Roo.isIE ? 6 : 5,
13991     minHeight: 80,
13992     minWidth: 200,
13993     minButtonWidth: 75,
13994     defaultButton: null,
13995     buttonAlign: "right",
13996     tabTag: 'div',
13997     firstShow: true,
13998
13999     /**
14000      * Sets the dialog title text
14001      * @param {String} text The title text to display
14002      * @return {Roo.BasicDialog} this
14003      */
14004     setTitle : function(text){
14005         this.header.update(text);
14006         return this;
14007     },
14008
14009     // private
14010     closeClick : function(){
14011         this.hide();
14012     },
14013
14014     // private
14015     collapseClick : function(){
14016         this[this.collapsed ? "expand" : "collapse"]();
14017     },
14018
14019     /**
14020      * Collapses the dialog to its minimized state (only the title bar is visible).
14021      * Equivalent to the user clicking the collapse dialog button.
14022      */
14023     collapse : function(){
14024         if(!this.collapsed){
14025             this.collapsed = true;
14026             this.el.addClass("x-dlg-collapsed");
14027             this.restoreHeight = this.el.getHeight();
14028             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14029         }
14030     },
14031
14032     /**
14033      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14034      * clicking the expand dialog button.
14035      */
14036     expand : function(){
14037         if(this.collapsed){
14038             this.collapsed = false;
14039             this.el.removeClass("x-dlg-collapsed");
14040             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14041         }
14042     },
14043
14044     /**
14045      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14046      * @return {Roo.TabPanel} The tabs component
14047      */
14048     initTabs : function(){
14049         var tabs = this.getTabs();
14050         while(tabs.getTab(0)){
14051             tabs.removeTab(0);
14052         }
14053         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14054             var dom = el.dom;
14055             tabs.addTab(Roo.id(dom), dom.title);
14056             dom.title = "";
14057         });
14058         tabs.activate(0);
14059         return tabs;
14060     },
14061
14062     // private
14063     beforeResize : function(){
14064         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14065     },
14066
14067     // private
14068     onResize : function(){
14069         this.refreshSize();
14070         this.syncBodyHeight();
14071         this.adjustAssets();
14072         this.focus();
14073         this.fireEvent("resize", this, this.size.width, this.size.height);
14074     },
14075
14076     // private
14077     onKeyDown : function(e){
14078         if(this.isVisible()){
14079             this.fireEvent("keydown", this, e);
14080         }
14081     },
14082
14083     /**
14084      * Resizes the dialog.
14085      * @param {Number} width
14086      * @param {Number} height
14087      * @return {Roo.BasicDialog} this
14088      */
14089     resizeTo : function(width, height){
14090         this.el.setSize(width, height);
14091         this.size = {width: width, height: height};
14092         this.syncBodyHeight();
14093         if(this.fixedcenter){
14094             this.center();
14095         }
14096         if(this.isVisible()){
14097             this.constrainXY();
14098             this.adjustAssets();
14099         }
14100         this.fireEvent("resize", this, width, height);
14101         return this;
14102     },
14103
14104
14105     /**
14106      * Resizes the dialog to fit the specified content size.
14107      * @param {Number} width
14108      * @param {Number} height
14109      * @return {Roo.BasicDialog} this
14110      */
14111     setContentSize : function(w, h){
14112         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14113         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14114         //if(!this.el.isBorderBox()){
14115             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14116             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14117         //}
14118         if(this.tabs){
14119             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14120             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14121         }
14122         this.resizeTo(w, h);
14123         return this;
14124     },
14125
14126     /**
14127      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14128      * executed in response to a particular key being pressed while the dialog is active.
14129      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14130      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14131      * @param {Function} fn The function to call
14132      * @param {Object} scope (optional) The scope of the function
14133      * @return {Roo.BasicDialog} this
14134      */
14135     addKeyListener : function(key, fn, scope){
14136         var keyCode, shift, ctrl, alt;
14137         if(typeof key == "object" && !(key instanceof Array)){
14138             keyCode = key["key"];
14139             shift = key["shift"];
14140             ctrl = key["ctrl"];
14141             alt = key["alt"];
14142         }else{
14143             keyCode = key;
14144         }
14145         var handler = function(dlg, e){
14146             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14147                 var k = e.getKey();
14148                 if(keyCode instanceof Array){
14149                     for(var i = 0, len = keyCode.length; i < len; i++){
14150                         if(keyCode[i] == k){
14151                           fn.call(scope || window, dlg, k, e);
14152                           return;
14153                         }
14154                     }
14155                 }else{
14156                     if(k == keyCode){
14157                         fn.call(scope || window, dlg, k, e);
14158                     }
14159                 }
14160             }
14161         };
14162         this.on("keydown", handler);
14163         return this;
14164     },
14165
14166     /**
14167      * Returns the TabPanel component (creates it if it doesn't exist).
14168      * Note: If you wish to simply check for the existence of tabs without creating them,
14169      * check for a null 'tabs' property.
14170      * @return {Roo.TabPanel} The tabs component
14171      */
14172     getTabs : function(){
14173         if(!this.tabs){
14174             this.el.addClass("x-dlg-auto-tabs");
14175             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14176             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14177         }
14178         return this.tabs;
14179     },
14180
14181     /**
14182      * Adds a button to the footer section of the dialog.
14183      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14184      * object or a valid Roo.DomHelper element config
14185      * @param {Function} handler The function called when the button is clicked
14186      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14187      * @return {Roo.Button} The new button
14188      */
14189     addButton : function(config, handler, scope){
14190         var dh = Roo.DomHelper;
14191         if(!this.footer){
14192             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14193         }
14194         if(!this.btnContainer){
14195             var tb = this.footer.createChild({
14196
14197                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14198                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14199             }, null, true);
14200             this.btnContainer = tb.firstChild.firstChild.firstChild;
14201         }
14202         var bconfig = {
14203             handler: handler,
14204             scope: scope,
14205             minWidth: this.minButtonWidth,
14206             hideParent:true
14207         };
14208         if(typeof config == "string"){
14209             bconfig.text = config;
14210         }else{
14211             if(config.tag){
14212                 bconfig.dhconfig = config;
14213             }else{
14214                 Roo.apply(bconfig, config);
14215             }
14216         }
14217         var fc = false;
14218         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14219             bconfig.position = Math.max(0, bconfig.position);
14220             fc = this.btnContainer.childNodes[bconfig.position];
14221         }
14222          
14223         var btn = new Roo.Button(
14224             fc ? 
14225                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14226                 : this.btnContainer.appendChild(document.createElement("td")),
14227             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14228             bconfig
14229         );
14230         this.syncBodyHeight();
14231         if(!this.buttons){
14232             /**
14233              * Array of all the buttons that have been added to this dialog via addButton
14234              * @type Array
14235              */
14236             this.buttons = [];
14237         }
14238         this.buttons.push(btn);
14239         return btn;
14240     },
14241
14242     /**
14243      * Sets the default button to be focused when the dialog is displayed.
14244      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14245      * @return {Roo.BasicDialog} this
14246      */
14247     setDefaultButton : function(btn){
14248         this.defaultButton = btn;
14249         return this;
14250     },
14251
14252     // private
14253     getHeaderFooterHeight : function(safe){
14254         var height = 0;
14255         if(this.header){
14256            height += this.header.getHeight();
14257         }
14258         if(this.footer){
14259            var fm = this.footer.getMargins();
14260             height += (this.footer.getHeight()+fm.top+fm.bottom);
14261         }
14262         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14263         height += this.centerBg.getPadding("tb");
14264         return height;
14265     },
14266
14267     // private
14268     syncBodyHeight : function()
14269     {
14270         var bd = this.body, // the text
14271             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14272             bw = this.bwrap;
14273         var height = this.size.height - this.getHeaderFooterHeight(false);
14274         bd.setHeight(height-bd.getMargins("tb"));
14275         var hh = this.header.getHeight();
14276         var h = this.size.height-hh;
14277         cb.setHeight(h);
14278         
14279         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14280         bw.setHeight(h-cb.getPadding("tb"));
14281         
14282         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14283         bd.setWidth(bw.getWidth(true));
14284         if(this.tabs){
14285             this.tabs.syncHeight();
14286             if(Roo.isIE){
14287                 this.tabs.el.repaint();
14288             }
14289         }
14290     },
14291
14292     /**
14293      * Restores the previous state of the dialog if Roo.state is configured.
14294      * @return {Roo.BasicDialog} this
14295      */
14296     restoreState : function(){
14297         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14298         if(box && box.width){
14299             this.xy = [box.x, box.y];
14300             this.resizeTo(box.width, box.height);
14301         }
14302         return this;
14303     },
14304
14305     // private
14306     beforeShow : function(){
14307         this.expand();
14308         if(this.fixedcenter){
14309             this.xy = this.el.getCenterXY(true);
14310         }
14311         if(this.modal){
14312             Roo.get(document.body).addClass("x-body-masked");
14313             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14314             this.mask.show();
14315         }
14316         this.constrainXY();
14317     },
14318
14319     // private
14320     animShow : function(){
14321         var b = Roo.get(this.animateTarget).getBox();
14322         this.proxy.setSize(b.width, b.height);
14323         this.proxy.setLocation(b.x, b.y);
14324         this.proxy.show();
14325         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14326                     true, .35, this.showEl.createDelegate(this));
14327     },
14328
14329     /**
14330      * Shows the dialog.
14331      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14332      * @return {Roo.BasicDialog} this
14333      */
14334     show : function(animateTarget){
14335         if (this.fireEvent("beforeshow", this) === false){
14336             return;
14337         }
14338         if(this.syncHeightBeforeShow){
14339             this.syncBodyHeight();
14340         }else if(this.firstShow){
14341             this.firstShow = false;
14342             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14343         }
14344         this.animateTarget = animateTarget || this.animateTarget;
14345         if(!this.el.isVisible()){
14346             this.beforeShow();
14347             if(this.animateTarget && Roo.get(this.animateTarget)){
14348                 this.animShow();
14349             }else{
14350                 this.showEl();
14351             }
14352         }
14353         return this;
14354     },
14355
14356     // private
14357     showEl : function(){
14358         this.proxy.hide();
14359         this.el.setXY(this.xy);
14360         this.el.show();
14361         this.adjustAssets(true);
14362         this.toFront();
14363         this.focus();
14364         // IE peekaboo bug - fix found by Dave Fenwick
14365         if(Roo.isIE){
14366             this.el.repaint();
14367         }
14368         this.fireEvent("show", this);
14369     },
14370
14371     /**
14372      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14373      * dialog itself will receive focus.
14374      */
14375     focus : function(){
14376         if(this.defaultButton){
14377             this.defaultButton.focus();
14378         }else{
14379             this.focusEl.focus();
14380         }
14381     },
14382
14383     // private
14384     constrainXY : function(){
14385         if(this.constraintoviewport !== false){
14386             if(!this.viewSize){
14387                 if(this.container){
14388                     var s = this.container.getSize();
14389                     this.viewSize = [s.width, s.height];
14390                 }else{
14391                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14392                 }
14393             }
14394             var s = Roo.get(this.container||document).getScroll();
14395
14396             var x = this.xy[0], y = this.xy[1];
14397             var w = this.size.width, h = this.size.height;
14398             var vw = this.viewSize[0], vh = this.viewSize[1];
14399             // only move it if it needs it
14400             var moved = false;
14401             // first validate right/bottom
14402             if(x + w > vw+s.left){
14403                 x = vw - w;
14404                 moved = true;
14405             }
14406             if(y + h > vh+s.top){
14407                 y = vh - h;
14408                 moved = true;
14409             }
14410             // then make sure top/left isn't negative
14411             if(x < s.left){
14412                 x = s.left;
14413                 moved = true;
14414             }
14415             if(y < s.top){
14416                 y = s.top;
14417                 moved = true;
14418             }
14419             if(moved){
14420                 // cache xy
14421                 this.xy = [x, y];
14422                 if(this.isVisible()){
14423                     this.el.setLocation(x, y);
14424                     this.adjustAssets();
14425                 }
14426             }
14427         }
14428     },
14429
14430     // private
14431     onDrag : function(){
14432         if(!this.proxyDrag){
14433             this.xy = this.el.getXY();
14434             this.adjustAssets();
14435         }
14436     },
14437
14438     // private
14439     adjustAssets : function(doShow){
14440         var x = this.xy[0], y = this.xy[1];
14441         var w = this.size.width, h = this.size.height;
14442         if(doShow === true){
14443             if(this.shadow){
14444                 this.shadow.show(this.el);
14445             }
14446             if(this.shim){
14447                 this.shim.show();
14448             }
14449         }
14450         if(this.shadow && this.shadow.isVisible()){
14451             this.shadow.show(this.el);
14452         }
14453         if(this.shim && this.shim.isVisible()){
14454             this.shim.setBounds(x, y, w, h);
14455         }
14456     },
14457
14458     // private
14459     adjustViewport : function(w, h){
14460         if(!w || !h){
14461             w = Roo.lib.Dom.getViewWidth();
14462             h = Roo.lib.Dom.getViewHeight();
14463         }
14464         // cache the size
14465         this.viewSize = [w, h];
14466         if(this.modal && this.mask.isVisible()){
14467             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14468             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14469         }
14470         if(this.isVisible()){
14471             this.constrainXY();
14472         }
14473     },
14474
14475     /**
14476      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14477      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14478      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14479      */
14480     destroy : function(removeEl){
14481         if(this.isVisible()){
14482             this.animateTarget = null;
14483             this.hide();
14484         }
14485         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14486         if(this.tabs){
14487             this.tabs.destroy(removeEl);
14488         }
14489         Roo.destroy(
14490              this.shim,
14491              this.proxy,
14492              this.resizer,
14493              this.close,
14494              this.mask
14495         );
14496         if(this.dd){
14497             this.dd.unreg();
14498         }
14499         if(this.buttons){
14500            for(var i = 0, len = this.buttons.length; i < len; i++){
14501                this.buttons[i].destroy();
14502            }
14503         }
14504         this.el.removeAllListeners();
14505         if(removeEl === true){
14506             this.el.update("");
14507             this.el.remove();
14508         }
14509         Roo.DialogManager.unregister(this);
14510     },
14511
14512     // private
14513     startMove : function(){
14514         if(this.proxyDrag){
14515             this.proxy.show();
14516         }
14517         if(this.constraintoviewport !== false){
14518             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14519         }
14520     },
14521
14522     // private
14523     endMove : function(){
14524         if(!this.proxyDrag){
14525             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14526         }else{
14527             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14528             this.proxy.hide();
14529         }
14530         this.refreshSize();
14531         this.adjustAssets();
14532         this.focus();
14533         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14534     },
14535
14536     /**
14537      * Brings this dialog to the front of any other visible dialogs
14538      * @return {Roo.BasicDialog} this
14539      */
14540     toFront : function(){
14541         Roo.DialogManager.bringToFront(this);
14542         return this;
14543     },
14544
14545     /**
14546      * Sends this dialog to the back (under) of any other visible dialogs
14547      * @return {Roo.BasicDialog} this
14548      */
14549     toBack : function(){
14550         Roo.DialogManager.sendToBack(this);
14551         return this;
14552     },
14553
14554     /**
14555      * Centers this dialog in the viewport
14556      * @return {Roo.BasicDialog} this
14557      */
14558     center : function(){
14559         var xy = this.el.getCenterXY(true);
14560         this.moveTo(xy[0], xy[1]);
14561         return this;
14562     },
14563
14564     /**
14565      * Moves the dialog's top-left corner to the specified point
14566      * @param {Number} x
14567      * @param {Number} y
14568      * @return {Roo.BasicDialog} this
14569      */
14570     moveTo : function(x, y){
14571         this.xy = [x,y];
14572         if(this.isVisible()){
14573             this.el.setXY(this.xy);
14574             this.adjustAssets();
14575         }
14576         return this;
14577     },
14578
14579     /**
14580      * Aligns the dialog to the specified element
14581      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14582      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14583      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14584      * @return {Roo.BasicDialog} this
14585      */
14586     alignTo : function(element, position, offsets){
14587         this.xy = this.el.getAlignToXY(element, position, offsets);
14588         if(this.isVisible()){
14589             this.el.setXY(this.xy);
14590             this.adjustAssets();
14591         }
14592         return this;
14593     },
14594
14595     /**
14596      * Anchors an element to another element and realigns it when the window is resized.
14597      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14598      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14599      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14600      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14601      * is a number, it is used as the buffer delay (defaults to 50ms).
14602      * @return {Roo.BasicDialog} this
14603      */
14604     anchorTo : function(el, alignment, offsets, monitorScroll){
14605         var action = function(){
14606             this.alignTo(el, alignment, offsets);
14607         };
14608         Roo.EventManager.onWindowResize(action, this);
14609         var tm = typeof monitorScroll;
14610         if(tm != 'undefined'){
14611             Roo.EventManager.on(window, 'scroll', action, this,
14612                 {buffer: tm == 'number' ? monitorScroll : 50});
14613         }
14614         action.call(this);
14615         return this;
14616     },
14617
14618     /**
14619      * Returns true if the dialog is visible
14620      * @return {Boolean}
14621      */
14622     isVisible : function(){
14623         return this.el.isVisible();
14624     },
14625
14626     // private
14627     animHide : function(callback){
14628         var b = Roo.get(this.animateTarget).getBox();
14629         this.proxy.show();
14630         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14631         this.el.hide();
14632         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14633                     this.hideEl.createDelegate(this, [callback]));
14634     },
14635
14636     /**
14637      * Hides the dialog.
14638      * @param {Function} callback (optional) Function to call when the dialog is hidden
14639      * @return {Roo.BasicDialog} this
14640      */
14641     hide : function(callback){
14642         if (this.fireEvent("beforehide", this) === false){
14643             return;
14644         }
14645         if(this.shadow){
14646             this.shadow.hide();
14647         }
14648         if(this.shim) {
14649           this.shim.hide();
14650         }
14651         // sometimes animateTarget seems to get set.. causing problems...
14652         // this just double checks..
14653         if(this.animateTarget && Roo.get(this.animateTarget)) {
14654            this.animHide(callback);
14655         }else{
14656             this.el.hide();
14657             this.hideEl(callback);
14658         }
14659         return this;
14660     },
14661
14662     // private
14663     hideEl : function(callback){
14664         this.proxy.hide();
14665         if(this.modal){
14666             this.mask.hide();
14667             Roo.get(document.body).removeClass("x-body-masked");
14668         }
14669         this.fireEvent("hide", this);
14670         if(typeof callback == "function"){
14671             callback();
14672         }
14673     },
14674
14675     // private
14676     hideAction : function(){
14677         this.setLeft("-10000px");
14678         this.setTop("-10000px");
14679         this.setStyle("visibility", "hidden");
14680     },
14681
14682     // private
14683     refreshSize : function(){
14684         this.size = this.el.getSize();
14685         this.xy = this.el.getXY();
14686         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14687     },
14688
14689     // private
14690     // z-index is managed by the DialogManager and may be overwritten at any time
14691     setZIndex : function(index){
14692         if(this.modal){
14693             this.mask.setStyle("z-index", index);
14694         }
14695         if(this.shim){
14696             this.shim.setStyle("z-index", ++index);
14697         }
14698         if(this.shadow){
14699             this.shadow.setZIndex(++index);
14700         }
14701         this.el.setStyle("z-index", ++index);
14702         if(this.proxy){
14703             this.proxy.setStyle("z-index", ++index);
14704         }
14705         if(this.resizer){
14706             this.resizer.proxy.setStyle("z-index", ++index);
14707         }
14708
14709         this.lastZIndex = index;
14710     },
14711
14712     /**
14713      * Returns the element for this dialog
14714      * @return {Roo.Element} The underlying dialog Element
14715      */
14716     getEl : function(){
14717         return this.el;
14718     }
14719 });
14720
14721 /**
14722  * @class Roo.DialogManager
14723  * Provides global access to BasicDialogs that have been created and
14724  * support for z-indexing (layering) multiple open dialogs.
14725  */
14726 Roo.DialogManager = function(){
14727     var list = {};
14728     var accessList = [];
14729     var front = null;
14730
14731     // private
14732     var sortDialogs = function(d1, d2){
14733         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14734     };
14735
14736     // private
14737     var orderDialogs = function(){
14738         accessList.sort(sortDialogs);
14739         var seed = Roo.DialogManager.zseed;
14740         for(var i = 0, len = accessList.length; i < len; i++){
14741             var dlg = accessList[i];
14742             if(dlg){
14743                 dlg.setZIndex(seed + (i*10));
14744             }
14745         }
14746     };
14747
14748     return {
14749         /**
14750          * The starting z-index for BasicDialogs (defaults to 9000)
14751          * @type Number The z-index value
14752          */
14753         zseed : 9000,
14754
14755         // private
14756         register : function(dlg){
14757             list[dlg.id] = dlg;
14758             accessList.push(dlg);
14759         },
14760
14761         // private
14762         unregister : function(dlg){
14763             delete list[dlg.id];
14764             var i=0;
14765             var len=0;
14766             if(!accessList.indexOf){
14767                 for(  i = 0, len = accessList.length; i < len; i++){
14768                     if(accessList[i] == dlg){
14769                         accessList.splice(i, 1);
14770                         return;
14771                     }
14772                 }
14773             }else{
14774                  i = accessList.indexOf(dlg);
14775                 if(i != -1){
14776                     accessList.splice(i, 1);
14777                 }
14778             }
14779         },
14780
14781         /**
14782          * Gets a registered dialog by id
14783          * @param {String/Object} id The id of the dialog or a dialog
14784          * @return {Roo.BasicDialog} this
14785          */
14786         get : function(id){
14787             return typeof id == "object" ? id : list[id];
14788         },
14789
14790         /**
14791          * Brings the specified dialog to the front
14792          * @param {String/Object} dlg The id of the dialog or a dialog
14793          * @return {Roo.BasicDialog} this
14794          */
14795         bringToFront : function(dlg){
14796             dlg = this.get(dlg);
14797             if(dlg != front){
14798                 front = dlg;
14799                 dlg._lastAccess = new Date().getTime();
14800                 orderDialogs();
14801             }
14802             return dlg;
14803         },
14804
14805         /**
14806          * Sends the specified dialog to the back
14807          * @param {String/Object} dlg The id of the dialog or a dialog
14808          * @return {Roo.BasicDialog} this
14809          */
14810         sendToBack : function(dlg){
14811             dlg = this.get(dlg);
14812             dlg._lastAccess = -(new Date().getTime());
14813             orderDialogs();
14814             return dlg;
14815         },
14816
14817         /**
14818          * Hides all dialogs
14819          */
14820         hideAll : function(){
14821             for(var id in list){
14822                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14823                     list[id].hide();
14824                 }
14825             }
14826         }
14827     };
14828 }();
14829
14830 /**
14831  * @class Roo.LayoutDialog
14832  * @extends Roo.BasicDialog
14833  * Dialog which provides adjustments for working with a layout in a Dialog.
14834  * Add your necessary layout config options to the dialog's config.<br>
14835  * Example usage (including a nested layout):
14836  * <pre><code>
14837 if(!dialog){
14838     dialog = new Roo.LayoutDialog("download-dlg", {
14839         modal: true,
14840         width:600,
14841         height:450,
14842         shadow:true,
14843         minWidth:500,
14844         minHeight:350,
14845         autoTabs:true,
14846         proxyDrag:true,
14847         // layout config merges with the dialog config
14848         center:{
14849             tabPosition: "top",
14850             alwaysShowTabs: true
14851         }
14852     });
14853     dialog.addKeyListener(27, dialog.hide, dialog);
14854     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14855     dialog.addButton("Build It!", this.getDownload, this);
14856
14857     // we can even add nested layouts
14858     var innerLayout = new Roo.BorderLayout("dl-inner", {
14859         east: {
14860             initialSize: 200,
14861             autoScroll:true,
14862             split:true
14863         },
14864         center: {
14865             autoScroll:true
14866         }
14867     });
14868     innerLayout.beginUpdate();
14869     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14870     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14871     innerLayout.endUpdate(true);
14872
14873     var layout = dialog.getLayout();
14874     layout.beginUpdate();
14875     layout.add("center", new Roo.ContentPanel("standard-panel",
14876                         {title: "Download the Source", fitToFrame:true}));
14877     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14878                {title: "Build your own roo.js"}));
14879     layout.getRegion("center").showPanel(sp);
14880     layout.endUpdate();
14881 }
14882 </code></pre>
14883     * @constructor
14884     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14885     * @param {Object} config configuration options
14886   */
14887 Roo.LayoutDialog = function(el, cfg){
14888     
14889     var config=  cfg;
14890     if (typeof(cfg) == 'undefined') {
14891         config = Roo.apply({}, el);
14892         // not sure why we use documentElement here.. - it should always be body.
14893         // IE7 borks horribly if we use documentElement.
14894         // webkit also does not like documentElement - it creates a body element...
14895         el = Roo.get( document.body || document.documentElement ).createChild();
14896         //config.autoCreate = true;
14897     }
14898     
14899     
14900     config.autoTabs = false;
14901     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14902     this.body.setStyle({overflow:"hidden", position:"relative"});
14903     this.layout = new Roo.BorderLayout(this.body.dom, config);
14904     this.layout.monitorWindowResize = false;
14905     this.el.addClass("x-dlg-auto-layout");
14906     // fix case when center region overwrites center function
14907     this.center = Roo.BasicDialog.prototype.center;
14908     this.on("show", this.layout.layout, this.layout, true);
14909     if (config.items) {
14910         var xitems = config.items;
14911         delete config.items;
14912         Roo.each(xitems, this.addxtype, this);
14913     }
14914     
14915     
14916 };
14917 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14918     /**
14919      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14920      * @deprecated
14921      */
14922     endUpdate : function(){
14923         this.layout.endUpdate();
14924     },
14925
14926     /**
14927      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14928      *  @deprecated
14929      */
14930     beginUpdate : function(){
14931         this.layout.beginUpdate();
14932     },
14933
14934     /**
14935      * Get the BorderLayout for this dialog
14936      * @return {Roo.BorderLayout}
14937      */
14938     getLayout : function(){
14939         return this.layout;
14940     },
14941
14942     showEl : function(){
14943         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14944         if(Roo.isIE7){
14945             this.layout.layout();
14946         }
14947     },
14948
14949     // private
14950     // Use the syncHeightBeforeShow config option to control this automatically
14951     syncBodyHeight : function(){
14952         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14953         if(this.layout){this.layout.layout();}
14954     },
14955     
14956       /**
14957      * Add an xtype element (actually adds to the layout.)
14958      * @return {Object} xdata xtype object data.
14959      */
14960     
14961     addxtype : function(c) {
14962         return this.layout.addxtype(c);
14963     }
14964 });/*
14965  * Based on:
14966  * Ext JS Library 1.1.1
14967  * Copyright(c) 2006-2007, Ext JS, LLC.
14968  *
14969  * Originally Released Under LGPL - original licence link has changed is not relivant.
14970  *
14971  * Fork - LGPL
14972  * <script type="text/javascript">
14973  */
14974  
14975 /**
14976  * @class Roo.MessageBox
14977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14978  * Example usage:
14979  *<pre><code>
14980 // Basic alert:
14981 Roo.Msg.alert('Status', 'Changes saved successfully.');
14982
14983 // Prompt for user data:
14984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14985     if (btn == 'ok'){
14986         // process text value...
14987     }
14988 });
14989
14990 // Show a dialog using config options:
14991 Roo.Msg.show({
14992    title:'Save Changes?',
14993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14994    buttons: Roo.Msg.YESNOCANCEL,
14995    fn: processResult,
14996    animEl: 'elId'
14997 });
14998 </code></pre>
14999  * @singleton
15000  */
15001 Roo.MessageBox = function(){
15002     var dlg, opt, mask, waitTimer;
15003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15004     var buttons, activeTextEl, bwidth;
15005
15006     // private
15007     var handleButton = function(button){
15008         dlg.hide();
15009         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15010     };
15011
15012     // private
15013     var handleHide = function(){
15014         if(opt && opt.cls){
15015             dlg.el.removeClass(opt.cls);
15016         }
15017         if(waitTimer){
15018             Roo.TaskMgr.stop(waitTimer);
15019             waitTimer = null;
15020         }
15021     };
15022
15023     // private
15024     var updateButtons = function(b){
15025         var width = 0;
15026         if(!b){
15027             buttons["ok"].hide();
15028             buttons["cancel"].hide();
15029             buttons["yes"].hide();
15030             buttons["no"].hide();
15031             dlg.footer.dom.style.display = 'none';
15032             return width;
15033         }
15034         dlg.footer.dom.style.display = '';
15035         for(var k in buttons){
15036             if(typeof buttons[k] != "function"){
15037                 if(b[k]){
15038                     buttons[k].show();
15039                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15040                     width += buttons[k].el.getWidth()+15;
15041                 }else{
15042                     buttons[k].hide();
15043                 }
15044             }
15045         }
15046         return width;
15047     };
15048
15049     // private
15050     var handleEsc = function(d, k, e){
15051         if(opt && opt.closable !== false){
15052             dlg.hide();
15053         }
15054         if(e){
15055             e.stopEvent();
15056         }
15057     };
15058
15059     return {
15060         /**
15061          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15062          * @return {Roo.BasicDialog} The BasicDialog element
15063          */
15064         getDialog : function(){
15065            if(!dlg){
15066                 dlg = new Roo.BasicDialog("x-msg-box", {
15067                     autoCreate : true,
15068                     shadow: true,
15069                     draggable: true,
15070                     resizable:false,
15071                     constraintoviewport:false,
15072                     fixedcenter:true,
15073                     collapsible : false,
15074                     shim:true,
15075                     modal: true,
15076                     width:400, height:100,
15077                     buttonAlign:"center",
15078                     closeClick : function(){
15079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15080                             handleButton("no");
15081                         }else{
15082                             handleButton("cancel");
15083                         }
15084                     }
15085                 });
15086                 dlg.on("hide", handleHide);
15087                 mask = dlg.mask;
15088                 dlg.addKeyListener(27, handleEsc);
15089                 buttons = {};
15090                 var bt = this.buttonText;
15091                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15092                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15093                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15094                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15095                 bodyEl = dlg.body.createChild({
15096
15097                     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>'
15098                 });
15099                 msgEl = bodyEl.dom.firstChild;
15100                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15101                 textboxEl.enableDisplayMode();
15102                 textboxEl.addKeyListener([10,13], function(){
15103                     if(dlg.isVisible() && opt && opt.buttons){
15104                         if(opt.buttons.ok){
15105                             handleButton("ok");
15106                         }else if(opt.buttons.yes){
15107                             handleButton("yes");
15108                         }
15109                     }
15110                 });
15111                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15112                 textareaEl.enableDisplayMode();
15113                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15114                 progressEl.enableDisplayMode();
15115                 var pf = progressEl.dom.firstChild;
15116                 if (pf) {
15117                     pp = Roo.get(pf.firstChild);
15118                     pp.setHeight(pf.offsetHeight);
15119                 }
15120                 
15121             }
15122             return dlg;
15123         },
15124
15125         /**
15126          * Updates the message box body text
15127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15128          * the XHTML-compliant non-breaking space character '&amp;#160;')
15129          * @return {Roo.MessageBox} This message box
15130          */
15131         updateText : function(text){
15132             if(!dlg.isVisible() && !opt.width){
15133                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15134             }
15135             msgEl.innerHTML = text || '&#160;';
15136       
15137             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15138             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15139             var w = Math.max(
15140                     Math.min(opt.width || cw , this.maxWidth), 
15141                     Math.max(opt.minWidth || this.minWidth, bwidth)
15142             );
15143             if(opt.prompt){
15144                 activeTextEl.setWidth(w);
15145             }
15146             if(dlg.isVisible()){
15147                 dlg.fixedcenter = false;
15148             }
15149             // to big, make it scroll. = But as usual stupid IE does not support
15150             // !important..
15151             
15152             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15153                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15154                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15155             } else {
15156                 bodyEl.dom.style.height = '';
15157                 bodyEl.dom.style.overflowY = '';
15158             }
15159             if (cw > w) {
15160                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15161             } else {
15162                 bodyEl.dom.style.overflowX = '';
15163             }
15164             
15165             dlg.setContentSize(w, bodyEl.getHeight());
15166             if(dlg.isVisible()){
15167                 dlg.fixedcenter = true;
15168             }
15169             return this;
15170         },
15171
15172         /**
15173          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15174          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15175          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15176          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15177          * @return {Roo.MessageBox} This message box
15178          */
15179         updateProgress : function(value, text){
15180             if(text){
15181                 this.updateText(text);
15182             }
15183             if (pp) { // weird bug on my firefox - for some reason this is not defined
15184                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15185             }
15186             return this;
15187         },        
15188
15189         /**
15190          * Returns true if the message box is currently displayed
15191          * @return {Boolean} True if the message box is visible, else false
15192          */
15193         isVisible : function(){
15194             return dlg && dlg.isVisible();  
15195         },
15196
15197         /**
15198          * Hides the message box if it is displayed
15199          */
15200         hide : function(){
15201             if(this.isVisible()){
15202                 dlg.hide();
15203             }  
15204         },
15205
15206         /**
15207          * Displays a new message box, or reinitializes an existing message box, based on the config options
15208          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15209          * The following config object properties are supported:
15210          * <pre>
15211 Property    Type             Description
15212 ----------  ---------------  ------------------------------------------------------------------------------------
15213 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15214                                    closes (defaults to undefined)
15215 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15216                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15217 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15218                                    progress and wait dialogs will ignore this property and always hide the
15219                                    close button as they can only be closed programmatically.
15220 cls               String           A custom CSS class to apply to the message box element
15221 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15222                                    displayed (defaults to 75)
15223 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15224                                    function will be btn (the name of the button that was clicked, if applicable,
15225                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15226                                    Progress and wait dialogs will ignore this option since they do not respond to
15227                                    user actions and can only be closed programmatically, so any required function
15228                                    should be called by the same code after it closes the dialog.
15229 icon              String           A CSS class that provides a background image to be used as an icon for
15230                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15231 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15232 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15233 modal             Boolean          False to allow user interaction with the page while the message box is
15234                                    displayed (defaults to true)
15235 msg               String           A string that will replace the existing message box body text (defaults
15236                                    to the XHTML-compliant non-breaking space character '&#160;')
15237 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15238 progress          Boolean          True to display a progress bar (defaults to false)
15239 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15240 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15241 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15242 title             String           The title text
15243 value             String           The string value to set into the active textbox element if displayed
15244 wait              Boolean          True to display a progress bar (defaults to false)
15245 width             Number           The width of the dialog in pixels
15246 </pre>
15247          *
15248          * Example usage:
15249          * <pre><code>
15250 Roo.Msg.show({
15251    title: 'Address',
15252    msg: 'Please enter your address:',
15253    width: 300,
15254    buttons: Roo.MessageBox.OKCANCEL,
15255    multiline: true,
15256    fn: saveAddress,
15257    animEl: 'addAddressBtn'
15258 });
15259 </code></pre>
15260          * @param {Object} config Configuration options
15261          * @return {Roo.MessageBox} This message box
15262          */
15263         show : function(options)
15264         {
15265             
15266             // this causes nightmares if you show one dialog after another
15267             // especially on callbacks..
15268              
15269             if(this.isVisible()){
15270                 
15271                 this.hide();
15272                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15273                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15274                 Roo.log("New Dialog Message:" +  options.msg )
15275                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15276                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15277                 
15278             }
15279             var d = this.getDialog();
15280             opt = options;
15281             d.setTitle(opt.title || "&#160;");
15282             d.close.setDisplayed(opt.closable !== false);
15283             activeTextEl = textboxEl;
15284             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15285             if(opt.prompt){
15286                 if(opt.multiline){
15287                     textboxEl.hide();
15288                     textareaEl.show();
15289                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15290                         opt.multiline : this.defaultTextHeight);
15291                     activeTextEl = textareaEl;
15292                 }else{
15293                     textboxEl.show();
15294                     textareaEl.hide();
15295                 }
15296             }else{
15297                 textboxEl.hide();
15298                 textareaEl.hide();
15299             }
15300             progressEl.setDisplayed(opt.progress === true);
15301             this.updateProgress(0);
15302             activeTextEl.dom.value = opt.value || "";
15303             if(opt.prompt){
15304                 dlg.setDefaultButton(activeTextEl);
15305             }else{
15306                 var bs = opt.buttons;
15307                 var db = null;
15308                 if(bs && bs.ok){
15309                     db = buttons["ok"];
15310                 }else if(bs && bs.yes){
15311                     db = buttons["yes"];
15312                 }
15313                 dlg.setDefaultButton(db);
15314             }
15315             bwidth = updateButtons(opt.buttons);
15316             this.updateText(opt.msg);
15317             if(opt.cls){
15318                 d.el.addClass(opt.cls);
15319             }
15320             d.proxyDrag = opt.proxyDrag === true;
15321             d.modal = opt.modal !== false;
15322             d.mask = opt.modal !== false ? mask : false;
15323             if(!d.isVisible()){
15324                 // force it to the end of the z-index stack so it gets a cursor in FF
15325                 document.body.appendChild(dlg.el.dom);
15326                 d.animateTarget = null;
15327                 d.show(options.animEl);
15328             }
15329             return this;
15330         },
15331
15332         /**
15333          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15334          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15335          * and closing the message box when the process is complete.
15336          * @param {String} title The title bar text
15337          * @param {String} msg The message box body text
15338          * @return {Roo.MessageBox} This message box
15339          */
15340         progress : function(title, msg){
15341             this.show({
15342                 title : title,
15343                 msg : msg,
15344                 buttons: false,
15345                 progress:true,
15346                 closable:false,
15347                 minWidth: this.minProgressWidth,
15348                 modal : true
15349             });
15350             return this;
15351         },
15352
15353         /**
15354          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15355          * If a callback function is passed it will be called after the user clicks the button, and the
15356          * id of the button that was clicked will be passed as the only parameter to the callback
15357          * (could also be the top-right close button).
15358          * @param {String} title The title bar text
15359          * @param {String} msg The message box body text
15360          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15361          * @param {Object} scope (optional) The scope of the callback function
15362          * @return {Roo.MessageBox} This message box
15363          */
15364         alert : function(title, msg, fn, scope){
15365             this.show({
15366                 title : title,
15367                 msg : msg,
15368                 buttons: this.OK,
15369                 fn: fn,
15370                 scope : scope,
15371                 modal : true
15372             });
15373             return this;
15374         },
15375
15376         /**
15377          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15378          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15379          * You are responsible for closing the message box when the process is complete.
15380          * @param {String} msg The message box body text
15381          * @param {String} title (optional) The title bar text
15382          * @return {Roo.MessageBox} This message box
15383          */
15384         wait : function(msg, title){
15385             this.show({
15386                 title : title,
15387                 msg : msg,
15388                 buttons: false,
15389                 closable:false,
15390                 progress:true,
15391                 modal:true,
15392                 width:300,
15393                 wait:true
15394             });
15395             waitTimer = Roo.TaskMgr.start({
15396                 run: function(i){
15397                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15398                 },
15399                 interval: 1000
15400             });
15401             return this;
15402         },
15403
15404         /**
15405          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15406          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15407          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15408          * @param {String} title The title bar text
15409          * @param {String} msg The message box body text
15410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15411          * @param {Object} scope (optional) The scope of the callback function
15412          * @return {Roo.MessageBox} This message box
15413          */
15414         confirm : function(title, msg, fn, scope){
15415             this.show({
15416                 title : title,
15417                 msg : msg,
15418                 buttons: this.YESNO,
15419                 fn: fn,
15420                 scope : scope,
15421                 modal : true
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15428          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15429          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15430          * (could also be the top-right close button) and the text that was entered will be passed as the two
15431          * parameters to the callback.
15432          * @param {String} title The title bar text
15433          * @param {String} msg The message box body text
15434          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15435          * @param {Object} scope (optional) The scope of the callback function
15436          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15437          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15438          * @return {Roo.MessageBox} This message box
15439          */
15440         prompt : function(title, msg, fn, scope, multiline){
15441             this.show({
15442                 title : title,
15443                 msg : msg,
15444                 buttons: this.OKCANCEL,
15445                 fn: fn,
15446                 minWidth:250,
15447                 scope : scope,
15448                 prompt:true,
15449                 multiline: multiline,
15450                 modal : true
15451             });
15452             return this;
15453         },
15454
15455         /**
15456          * Button config that displays a single OK button
15457          * @type Object
15458          */
15459         OK : {ok:true},
15460         /**
15461          * Button config that displays Yes and No buttons
15462          * @type Object
15463          */
15464         YESNO : {yes:true, no:true},
15465         /**
15466          * Button config that displays OK and Cancel buttons
15467          * @type Object
15468          */
15469         OKCANCEL : {ok:true, cancel:true},
15470         /**
15471          * Button config that displays Yes, No and Cancel buttons
15472          * @type Object
15473          */
15474         YESNOCANCEL : {yes:true, no:true, cancel:true},
15475
15476         /**
15477          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15478          * @type Number
15479          */
15480         defaultTextHeight : 75,
15481         /**
15482          * The maximum width in pixels of the message box (defaults to 600)
15483          * @type Number
15484          */
15485         maxWidth : 600,
15486         /**
15487          * The minimum width in pixels of the message box (defaults to 100)
15488          * @type Number
15489          */
15490         minWidth : 100,
15491         /**
15492          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15493          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15494          * @type Number
15495          */
15496         minProgressWidth : 250,
15497         /**
15498          * An object containing the default button text strings that can be overriden for localized language support.
15499          * Supported properties are: ok, cancel, yes and no.
15500          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15501          * @type Object
15502          */
15503         buttonText : {
15504             ok : "OK",
15505             cancel : "Cancel",
15506             yes : "Yes",
15507             no : "No"
15508         }
15509     };
15510 }();
15511
15512 /**
15513  * Shorthand for {@link Roo.MessageBox}
15514  */
15515 Roo.Msg = Roo.MessageBox;/*
15516  * Based on:
15517  * Ext JS Library 1.1.1
15518  * Copyright(c) 2006-2007, Ext JS, LLC.
15519  *
15520  * Originally Released Under LGPL - original licence link has changed is not relivant.
15521  *
15522  * Fork - LGPL
15523  * <script type="text/javascript">
15524  */
15525 /**
15526  * @class Roo.QuickTips
15527  * Provides attractive and customizable tooltips for any element.
15528  * @singleton
15529  */
15530 Roo.QuickTips = function(){
15531     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15532     var ce, bd, xy, dd;
15533     var visible = false, disabled = true, inited = false;
15534     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15535     
15536     var onOver = function(e){
15537         if(disabled){
15538             return;
15539         }
15540         var t = e.getTarget();
15541         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15542             return;
15543         }
15544         if(ce && t == ce.el){
15545             clearTimeout(hideProc);
15546             return;
15547         }
15548         if(t && tagEls[t.id]){
15549             tagEls[t.id].el = t;
15550             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15551             return;
15552         }
15553         var ttp, et = Roo.fly(t);
15554         var ns = cfg.namespace;
15555         if(tm.interceptTitles && t.title){
15556             ttp = t.title;
15557             t.qtip = ttp;
15558             t.removeAttribute("title");
15559             e.preventDefault();
15560         }else{
15561             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15562         }
15563         if(ttp){
15564             showProc = show.defer(tm.showDelay, tm, [{
15565                 el: t, 
15566                 text: ttp, 
15567                 width: et.getAttributeNS(ns, cfg.width),
15568                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15569                 title: et.getAttributeNS(ns, cfg.title),
15570                     cls: et.getAttributeNS(ns, cfg.cls)
15571             }]);
15572         }
15573     };
15574     
15575     var onOut = function(e){
15576         clearTimeout(showProc);
15577         var t = e.getTarget();
15578         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15579             hideProc = setTimeout(hide, tm.hideDelay);
15580         }
15581     };
15582     
15583     var onMove = function(e){
15584         if(disabled){
15585             return;
15586         }
15587         xy = e.getXY();
15588         xy[1] += 18;
15589         if(tm.trackMouse && ce){
15590             el.setXY(xy);
15591         }
15592     };
15593     
15594     var onDown = function(e){
15595         clearTimeout(showProc);
15596         clearTimeout(hideProc);
15597         if(!e.within(el)){
15598             if(tm.hideOnClick){
15599                 hide();
15600                 tm.disable();
15601                 tm.enable.defer(100, tm);
15602             }
15603         }
15604     };
15605     
15606     var getPad = function(){
15607         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15608     };
15609
15610     var show = function(o){
15611         if(disabled){
15612             return;
15613         }
15614         clearTimeout(dismissProc);
15615         ce = o;
15616         if(removeCls){ // in case manually hidden
15617             el.removeClass(removeCls);
15618             removeCls = null;
15619         }
15620         if(ce.cls){
15621             el.addClass(ce.cls);
15622             removeCls = ce.cls;
15623         }
15624         if(ce.title){
15625             tipTitle.update(ce.title);
15626             tipTitle.show();
15627         }else{
15628             tipTitle.update('');
15629             tipTitle.hide();
15630         }
15631         el.dom.style.width  = tm.maxWidth+'px';
15632         //tipBody.dom.style.width = '';
15633         tipBodyText.update(o.text);
15634         var p = getPad(), w = ce.width;
15635         if(!w){
15636             var td = tipBodyText.dom;
15637             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15638             if(aw > tm.maxWidth){
15639                 w = tm.maxWidth;
15640             }else if(aw < tm.minWidth){
15641                 w = tm.minWidth;
15642             }else{
15643                 w = aw;
15644             }
15645         }
15646         //tipBody.setWidth(w);
15647         el.setWidth(parseInt(w, 10) + p);
15648         if(ce.autoHide === false){
15649             close.setDisplayed(true);
15650             if(dd){
15651                 dd.unlock();
15652             }
15653         }else{
15654             close.setDisplayed(false);
15655             if(dd){
15656                 dd.lock();
15657             }
15658         }
15659         if(xy){
15660             el.avoidY = xy[1]-18;
15661             el.setXY(xy);
15662         }
15663         if(tm.animate){
15664             el.setOpacity(.1);
15665             el.setStyle("visibility", "visible");
15666             el.fadeIn({callback: afterShow});
15667         }else{
15668             afterShow();
15669         }
15670     };
15671     
15672     var afterShow = function(){
15673         if(ce){
15674             el.show();
15675             esc.enable();
15676             if(tm.autoDismiss && ce.autoHide !== false){
15677                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15678             }
15679         }
15680     };
15681     
15682     var hide = function(noanim){
15683         clearTimeout(dismissProc);
15684         clearTimeout(hideProc);
15685         ce = null;
15686         if(el.isVisible()){
15687             esc.disable();
15688             if(noanim !== true && tm.animate){
15689                 el.fadeOut({callback: afterHide});
15690             }else{
15691                 afterHide();
15692             } 
15693         }
15694     };
15695     
15696     var afterHide = function(){
15697         el.hide();
15698         if(removeCls){
15699             el.removeClass(removeCls);
15700             removeCls = null;
15701         }
15702     };
15703     
15704     return {
15705         /**
15706         * @cfg {Number} minWidth
15707         * The minimum width of the quick tip (defaults to 40)
15708         */
15709        minWidth : 40,
15710         /**
15711         * @cfg {Number} maxWidth
15712         * The maximum width of the quick tip (defaults to 300)
15713         */
15714        maxWidth : 300,
15715         /**
15716         * @cfg {Boolean} interceptTitles
15717         * True to automatically use the element's DOM title value if available (defaults to false)
15718         */
15719        interceptTitles : false,
15720         /**
15721         * @cfg {Boolean} trackMouse
15722         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15723         */
15724        trackMouse : false,
15725         /**
15726         * @cfg {Boolean} hideOnClick
15727         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15728         */
15729        hideOnClick : true,
15730         /**
15731         * @cfg {Number} showDelay
15732         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15733         */
15734        showDelay : 500,
15735         /**
15736         * @cfg {Number} hideDelay
15737         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15738         */
15739        hideDelay : 200,
15740         /**
15741         * @cfg {Boolean} autoHide
15742         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15743         * Used in conjunction with hideDelay.
15744         */
15745        autoHide : true,
15746         /**
15747         * @cfg {Boolean}
15748         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15749         * (defaults to true).  Used in conjunction with autoDismissDelay.
15750         */
15751        autoDismiss : true,
15752         /**
15753         * @cfg {Number}
15754         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15755         */
15756        autoDismissDelay : 5000,
15757        /**
15758         * @cfg {Boolean} animate
15759         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15760         */
15761        animate : false,
15762
15763        /**
15764         * @cfg {String} title
15765         * Title text to display (defaults to '').  This can be any valid HTML markup.
15766         */
15767         title: '',
15768        /**
15769         * @cfg {String} text
15770         * Body text to display (defaults to '').  This can be any valid HTML markup.
15771         */
15772         text : '',
15773        /**
15774         * @cfg {String} cls
15775         * A CSS class to apply to the base quick tip element (defaults to '').
15776         */
15777         cls : '',
15778        /**
15779         * @cfg {Number} width
15780         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15781         * minWidth or maxWidth.
15782         */
15783         width : null,
15784
15785     /**
15786      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15787      * or display QuickTips in a page.
15788      */
15789        init : function(){
15790           tm = Roo.QuickTips;
15791           cfg = tm.tagConfig;
15792           if(!inited){
15793               if(!Roo.isReady){ // allow calling of init() before onReady
15794                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15795                   return;
15796               }
15797               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15798               el.fxDefaults = {stopFx: true};
15799               // maximum custom styling
15800               //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>');
15801               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>');              
15802               tipTitle = el.child('h3');
15803               tipTitle.enableDisplayMode("block");
15804               tipBody = el.child('div.x-tip-bd');
15805               tipBodyText = el.child('div.x-tip-bd-inner');
15806               //bdLeft = el.child('div.x-tip-bd-left');
15807               //bdRight = el.child('div.x-tip-bd-right');
15808               close = el.child('div.x-tip-close');
15809               close.enableDisplayMode("block");
15810               close.on("click", hide);
15811               var d = Roo.get(document);
15812               d.on("mousedown", onDown);
15813               d.on("mouseover", onOver);
15814               d.on("mouseout", onOut);
15815               d.on("mousemove", onMove);
15816               esc = d.addKeyListener(27, hide);
15817               esc.disable();
15818               if(Roo.dd.DD){
15819                   dd = el.initDD("default", null, {
15820                       onDrag : function(){
15821                           el.sync();  
15822                       }
15823                   });
15824                   dd.setHandleElId(tipTitle.id);
15825                   dd.lock();
15826               }
15827               inited = true;
15828           }
15829           this.enable(); 
15830        },
15831
15832     /**
15833      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15834      * are supported:
15835      * <pre>
15836 Property    Type                   Description
15837 ----------  ---------------------  ------------------------------------------------------------------------
15838 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15839      * </ul>
15840      * @param {Object} config The config object
15841      */
15842        register : function(config){
15843            var cs = config instanceof Array ? config : arguments;
15844            for(var i = 0, len = cs.length; i < len; i++) {
15845                var c = cs[i];
15846                var target = c.target;
15847                if(target){
15848                    if(target instanceof Array){
15849                        for(var j = 0, jlen = target.length; j < jlen; j++){
15850                            tagEls[target[j]] = c;
15851                        }
15852                    }else{
15853                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15854                    }
15855                }
15856            }
15857        },
15858
15859     /**
15860      * Removes this quick tip from its element and destroys it.
15861      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15862      */
15863        unregister : function(el){
15864            delete tagEls[Roo.id(el)];
15865        },
15866
15867     /**
15868      * Enable this quick tip.
15869      */
15870        enable : function(){
15871            if(inited && disabled){
15872                locks.pop();
15873                if(locks.length < 1){
15874                    disabled = false;
15875                }
15876            }
15877        },
15878
15879     /**
15880      * Disable this quick tip.
15881      */
15882        disable : function(){
15883           disabled = true;
15884           clearTimeout(showProc);
15885           clearTimeout(hideProc);
15886           clearTimeout(dismissProc);
15887           if(ce){
15888               hide(true);
15889           }
15890           locks.push(1);
15891        },
15892
15893     /**
15894      * Returns true if the quick tip is enabled, else false.
15895      */
15896        isEnabled : function(){
15897             return !disabled;
15898        },
15899
15900         // private
15901        tagConfig : {
15902            namespace : "ext",
15903            attribute : "qtip",
15904            width : "width",
15905            target : "target",
15906            title : "qtitle",
15907            hide : "hide",
15908            cls : "qclass"
15909        }
15910    };
15911 }();
15912
15913 // backwards compat
15914 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15915  * Based on:
15916  * Ext JS Library 1.1.1
15917  * Copyright(c) 2006-2007, Ext JS, LLC.
15918  *
15919  * Originally Released Under LGPL - original licence link has changed is not relivant.
15920  *
15921  * Fork - LGPL
15922  * <script type="text/javascript">
15923  */
15924  
15925
15926 /**
15927  * @class Roo.tree.TreePanel
15928  * @extends Roo.data.Tree
15929
15930  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15931  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15932  * @cfg {Boolean} enableDD true to enable drag and drop
15933  * @cfg {Boolean} enableDrag true to enable just drag
15934  * @cfg {Boolean} enableDrop true to enable just drop
15935  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15936  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15937  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15938  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15939  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15940  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15941  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15942  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15943  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15944  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15945  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15946  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15947  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15948  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15949  * @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>
15950  * @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>
15951  * 
15952  * @constructor
15953  * @param {String/HTMLElement/Element} el The container element
15954  * @param {Object} config
15955  */
15956 Roo.tree.TreePanel = function(el, config){
15957     var root = false;
15958     var loader = false;
15959     if (config.root) {
15960         root = config.root;
15961         delete config.root;
15962     }
15963     if (config.loader) {
15964         loader = config.loader;
15965         delete config.loader;
15966     }
15967     
15968     Roo.apply(this, config);
15969     Roo.tree.TreePanel.superclass.constructor.call(this);
15970     this.el = Roo.get(el);
15971     this.el.addClass('x-tree');
15972     //console.log(root);
15973     if (root) {
15974         this.setRootNode( Roo.factory(root, Roo.tree));
15975     }
15976     if (loader) {
15977         this.loader = Roo.factory(loader, Roo.tree);
15978     }
15979    /**
15980     * Read-only. The id of the container element becomes this TreePanel's id.
15981     */
15982     this.id = this.el.id;
15983     this.addEvents({
15984         /**
15985         * @event beforeload
15986         * Fires before a node is loaded, return false to cancel
15987         * @param {Node} node The node being loaded
15988         */
15989         "beforeload" : true,
15990         /**
15991         * @event load
15992         * Fires when a node is loaded
15993         * @param {Node} node The node that was loaded
15994         */
15995         "load" : true,
15996         /**
15997         * @event textchange
15998         * Fires when the text for a node is changed
15999         * @param {Node} node The node
16000         * @param {String} text The new text
16001         * @param {String} oldText The old text
16002         */
16003         "textchange" : true,
16004         /**
16005         * @event beforeexpand
16006         * Fires before a node is expanded, return false to cancel.
16007         * @param {Node} node The node
16008         * @param {Boolean} deep
16009         * @param {Boolean} anim
16010         */
16011         "beforeexpand" : true,
16012         /**
16013         * @event beforecollapse
16014         * Fires before a node is collapsed, return false to cancel.
16015         * @param {Node} node The node
16016         * @param {Boolean} deep
16017         * @param {Boolean} anim
16018         */
16019         "beforecollapse" : true,
16020         /**
16021         * @event expand
16022         * Fires when a node is expanded
16023         * @param {Node} node The node
16024         */
16025         "expand" : true,
16026         /**
16027         * @event disabledchange
16028         * Fires when the disabled status of a node changes
16029         * @param {Node} node The node
16030         * @param {Boolean} disabled
16031         */
16032         "disabledchange" : true,
16033         /**
16034         * @event collapse
16035         * Fires when a node is collapsed
16036         * @param {Node} node The node
16037         */
16038         "collapse" : true,
16039         /**
16040         * @event beforeclick
16041         * Fires before click processing on a node. Return false to cancel the default action.
16042         * @param {Node} node The node
16043         * @param {Roo.EventObject} e The event object
16044         */
16045         "beforeclick":true,
16046         /**
16047         * @event checkchange
16048         * Fires when a node with a checkbox's checked property changes
16049         * @param {Node} this This node
16050         * @param {Boolean} checked
16051         */
16052         "checkchange":true,
16053         /**
16054         * @event click
16055         * Fires when a node is clicked
16056         * @param {Node} node The node
16057         * @param {Roo.EventObject} e The event object
16058         */
16059         "click":true,
16060         /**
16061         * @event dblclick
16062         * Fires when a node is double clicked
16063         * @param {Node} node The node
16064         * @param {Roo.EventObject} e The event object
16065         */
16066         "dblclick":true,
16067         /**
16068         * @event contextmenu
16069         * Fires when a node is right clicked
16070         * @param {Node} node The node
16071         * @param {Roo.EventObject} e The event object
16072         */
16073         "contextmenu":true,
16074         /**
16075         * @event beforechildrenrendered
16076         * Fires right before the child nodes for a node are rendered
16077         * @param {Node} node The node
16078         */
16079         "beforechildrenrendered":true,
16080         /**
16081         * @event startdrag
16082         * Fires when a node starts being dragged
16083         * @param {Roo.tree.TreePanel} this
16084         * @param {Roo.tree.TreeNode} node
16085         * @param {event} e The raw browser event
16086         */ 
16087        "startdrag" : true,
16088        /**
16089         * @event enddrag
16090         * Fires when a drag operation is complete
16091         * @param {Roo.tree.TreePanel} this
16092         * @param {Roo.tree.TreeNode} node
16093         * @param {event} e The raw browser event
16094         */
16095        "enddrag" : true,
16096        /**
16097         * @event dragdrop
16098         * Fires when a dragged node is dropped on a valid DD target
16099         * @param {Roo.tree.TreePanel} this
16100         * @param {Roo.tree.TreeNode} node
16101         * @param {DD} dd The dd it was dropped on
16102         * @param {event} e The raw browser event
16103         */
16104        "dragdrop" : true,
16105        /**
16106         * @event beforenodedrop
16107         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16108         * passed to handlers has the following properties:<br />
16109         * <ul style="padding:5px;padding-left:16px;">
16110         * <li>tree - The TreePanel</li>
16111         * <li>target - The node being targeted for the drop</li>
16112         * <li>data - The drag data from the drag source</li>
16113         * <li>point - The point of the drop - append, above or below</li>
16114         * <li>source - The drag source</li>
16115         * <li>rawEvent - Raw mouse event</li>
16116         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16117         * to be inserted by setting them on this object.</li>
16118         * <li>cancel - Set this to true to cancel the drop.</li>
16119         * </ul>
16120         * @param {Object} dropEvent
16121         */
16122        "beforenodedrop" : true,
16123        /**
16124         * @event nodedrop
16125         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16126         * passed to handlers has the following properties:<br />
16127         * <ul style="padding:5px;padding-left:16px;">
16128         * <li>tree - The TreePanel</li>
16129         * <li>target - The node being targeted for the drop</li>
16130         * <li>data - The drag data from the drag source</li>
16131         * <li>point - The point of the drop - append, above or below</li>
16132         * <li>source - The drag source</li>
16133         * <li>rawEvent - Raw mouse event</li>
16134         * <li>dropNode - Dropped node(s).</li>
16135         * </ul>
16136         * @param {Object} dropEvent
16137         */
16138        "nodedrop" : true,
16139         /**
16140         * @event nodedragover
16141         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16142         * passed to handlers has the following properties:<br />
16143         * <ul style="padding:5px;padding-left:16px;">
16144         * <li>tree - The TreePanel</li>
16145         * <li>target - The node being targeted for the drop</li>
16146         * <li>data - The drag data from the drag source</li>
16147         * <li>point - The point of the drop - append, above or below</li>
16148         * <li>source - The drag source</li>
16149         * <li>rawEvent - Raw mouse event</li>
16150         * <li>dropNode - Drop node(s) provided by the source.</li>
16151         * <li>cancel - Set this to true to signal drop not allowed.</li>
16152         * </ul>
16153         * @param {Object} dragOverEvent
16154         */
16155        "nodedragover" : true
16156         
16157     });
16158     if(this.singleExpand){
16159        this.on("beforeexpand", this.restrictExpand, this);
16160     }
16161     if (this.editor) {
16162         this.editor.tree = this;
16163         this.editor = Roo.factory(this.editor, Roo.tree);
16164     }
16165     
16166     if (this.selModel) {
16167         this.selModel = Roo.factory(this.selModel, Roo.tree);
16168     }
16169    
16170 };
16171 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16172     rootVisible : true,
16173     animate: Roo.enableFx,
16174     lines : true,
16175     enableDD : false,
16176     hlDrop : Roo.enableFx,
16177   
16178     renderer: false,
16179     
16180     rendererTip: false,
16181     // private
16182     restrictExpand : function(node){
16183         var p = node.parentNode;
16184         if(p){
16185             if(p.expandedChild && p.expandedChild.parentNode == p){
16186                 p.expandedChild.collapse();
16187             }
16188             p.expandedChild = node;
16189         }
16190     },
16191
16192     // private override
16193     setRootNode : function(node){
16194         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16195         if(!this.rootVisible){
16196             node.ui = new Roo.tree.RootTreeNodeUI(node);
16197         }
16198         return node;
16199     },
16200
16201     /**
16202      * Returns the container element for this TreePanel
16203      */
16204     getEl : function(){
16205         return this.el;
16206     },
16207
16208     /**
16209      * Returns the default TreeLoader for this TreePanel
16210      */
16211     getLoader : function(){
16212         return this.loader;
16213     },
16214
16215     /**
16216      * Expand all nodes
16217      */
16218     expandAll : function(){
16219         this.root.expand(true);
16220     },
16221
16222     /**
16223      * Collapse all nodes
16224      */
16225     collapseAll : function(){
16226         this.root.collapse(true);
16227     },
16228
16229     /**
16230      * Returns the selection model used by this TreePanel
16231      */
16232     getSelectionModel : function(){
16233         if(!this.selModel){
16234             this.selModel = new Roo.tree.DefaultSelectionModel();
16235         }
16236         return this.selModel;
16237     },
16238
16239     /**
16240      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16241      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16242      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16243      * @return {Array}
16244      */
16245     getChecked : function(a, startNode){
16246         startNode = startNode || this.root;
16247         var r = [];
16248         var f = function(){
16249             if(this.attributes.checked){
16250                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16251             }
16252         }
16253         startNode.cascade(f);
16254         return r;
16255     },
16256
16257     /**
16258      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16259      * @param {String} path
16260      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16261      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16262      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16263      */
16264     expandPath : function(path, attr, callback){
16265         attr = attr || "id";
16266         var keys = path.split(this.pathSeparator);
16267         var curNode = this.root;
16268         if(curNode.attributes[attr] != keys[1]){ // invalid root
16269             if(callback){
16270                 callback(false, null);
16271             }
16272             return;
16273         }
16274         var index = 1;
16275         var f = function(){
16276             if(++index == keys.length){
16277                 if(callback){
16278                     callback(true, curNode);
16279                 }
16280                 return;
16281             }
16282             var c = curNode.findChild(attr, keys[index]);
16283             if(!c){
16284                 if(callback){
16285                     callback(false, curNode);
16286                 }
16287                 return;
16288             }
16289             curNode = c;
16290             c.expand(false, false, f);
16291         };
16292         curNode.expand(false, false, f);
16293     },
16294
16295     /**
16296      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16297      * @param {String} path
16298      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16299      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16300      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16301      */
16302     selectPath : function(path, attr, callback){
16303         attr = attr || "id";
16304         var keys = path.split(this.pathSeparator);
16305         var v = keys.pop();
16306         if(keys.length > 0){
16307             var f = function(success, node){
16308                 if(success && node){
16309                     var n = node.findChild(attr, v);
16310                     if(n){
16311                         n.select();
16312                         if(callback){
16313                             callback(true, n);
16314                         }
16315                     }else if(callback){
16316                         callback(false, n);
16317                     }
16318                 }else{
16319                     if(callback){
16320                         callback(false, n);
16321                     }
16322                 }
16323             };
16324             this.expandPath(keys.join(this.pathSeparator), attr, f);
16325         }else{
16326             this.root.select();
16327             if(callback){
16328                 callback(true, this.root);
16329             }
16330         }
16331     },
16332
16333     getTreeEl : function(){
16334         return this.el;
16335     },
16336
16337     /**
16338      * Trigger rendering of this TreePanel
16339      */
16340     render : function(){
16341         if (this.innerCt) {
16342             return this; // stop it rendering more than once!!
16343         }
16344         
16345         this.innerCt = this.el.createChild({tag:"ul",
16346                cls:"x-tree-root-ct " +
16347                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16348
16349         if(this.containerScroll){
16350             Roo.dd.ScrollManager.register(this.el);
16351         }
16352         if((this.enableDD || this.enableDrop) && !this.dropZone){
16353            /**
16354             * The dropZone used by this tree if drop is enabled
16355             * @type Roo.tree.TreeDropZone
16356             */
16357              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16358                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16359            });
16360         }
16361         if((this.enableDD || this.enableDrag) && !this.dragZone){
16362            /**
16363             * The dragZone used by this tree if drag is enabled
16364             * @type Roo.tree.TreeDragZone
16365             */
16366             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16367                ddGroup: this.ddGroup || "TreeDD",
16368                scroll: this.ddScroll
16369            });
16370         }
16371         this.getSelectionModel().init(this);
16372         if (!this.root) {
16373             Roo.log("ROOT not set in tree");
16374             return this;
16375         }
16376         this.root.render();
16377         if(!this.rootVisible){
16378             this.root.renderChildren();
16379         }
16380         return this;
16381     }
16382 });/*
16383  * Based on:
16384  * Ext JS Library 1.1.1
16385  * Copyright(c) 2006-2007, Ext JS, LLC.
16386  *
16387  * Originally Released Under LGPL - original licence link has changed is not relivant.
16388  *
16389  * Fork - LGPL
16390  * <script type="text/javascript">
16391  */
16392  
16393
16394 /**
16395  * @class Roo.tree.DefaultSelectionModel
16396  * @extends Roo.util.Observable
16397  * The default single selection for a TreePanel.
16398  * @param {Object} cfg Configuration
16399  */
16400 Roo.tree.DefaultSelectionModel = function(cfg){
16401    this.selNode = null;
16402    
16403    
16404    
16405    this.addEvents({
16406        /**
16407         * @event selectionchange
16408         * Fires when the selected node changes
16409         * @param {DefaultSelectionModel} this
16410         * @param {TreeNode} node the new selection
16411         */
16412        "selectionchange" : true,
16413
16414        /**
16415         * @event beforeselect
16416         * Fires before the selected node changes, return false to cancel the change
16417         * @param {DefaultSelectionModel} this
16418         * @param {TreeNode} node the new selection
16419         * @param {TreeNode} node the old selection
16420         */
16421        "beforeselect" : true
16422    });
16423    
16424     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16425 };
16426
16427 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16428     init : function(tree){
16429         this.tree = tree;
16430         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16431         tree.on("click", this.onNodeClick, this);
16432     },
16433     
16434     onNodeClick : function(node, e){
16435         if (e.ctrlKey && this.selNode == node)  {
16436             this.unselect(node);
16437             return;
16438         }
16439         this.select(node);
16440     },
16441     
16442     /**
16443      * Select a node.
16444      * @param {TreeNode} node The node to select
16445      * @return {TreeNode} The selected node
16446      */
16447     select : function(node){
16448         var last = this.selNode;
16449         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16450             if(last){
16451                 last.ui.onSelectedChange(false);
16452             }
16453             this.selNode = node;
16454             node.ui.onSelectedChange(true);
16455             this.fireEvent("selectionchange", this, node, last);
16456         }
16457         return node;
16458     },
16459     
16460     /**
16461      * Deselect a node.
16462      * @param {TreeNode} node The node to unselect
16463      */
16464     unselect : function(node){
16465         if(this.selNode == node){
16466             this.clearSelections();
16467         }    
16468     },
16469     
16470     /**
16471      * Clear all selections
16472      */
16473     clearSelections : function(){
16474         var n = this.selNode;
16475         if(n){
16476             n.ui.onSelectedChange(false);
16477             this.selNode = null;
16478             this.fireEvent("selectionchange", this, null);
16479         }
16480         return n;
16481     },
16482     
16483     /**
16484      * Get the selected node
16485      * @return {TreeNode} The selected node
16486      */
16487     getSelectedNode : function(){
16488         return this.selNode;    
16489     },
16490     
16491     /**
16492      * Returns true if the node is selected
16493      * @param {TreeNode} node The node to check
16494      * @return {Boolean}
16495      */
16496     isSelected : function(node){
16497         return this.selNode == node;  
16498     },
16499
16500     /**
16501      * Selects the node above the selected node in the tree, intelligently walking the nodes
16502      * @return TreeNode The new selection
16503      */
16504     selectPrevious : function(){
16505         var s = this.selNode || this.lastSelNode;
16506         if(!s){
16507             return null;
16508         }
16509         var ps = s.previousSibling;
16510         if(ps){
16511             if(!ps.isExpanded() || ps.childNodes.length < 1){
16512                 return this.select(ps);
16513             } else{
16514                 var lc = ps.lastChild;
16515                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16516                     lc = lc.lastChild;
16517                 }
16518                 return this.select(lc);
16519             }
16520         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16521             return this.select(s.parentNode);
16522         }
16523         return null;
16524     },
16525
16526     /**
16527      * Selects the node above the selected node in the tree, intelligently walking the nodes
16528      * @return TreeNode The new selection
16529      */
16530     selectNext : function(){
16531         var s = this.selNode || this.lastSelNode;
16532         if(!s){
16533             return null;
16534         }
16535         if(s.firstChild && s.isExpanded()){
16536              return this.select(s.firstChild);
16537          }else if(s.nextSibling){
16538              return this.select(s.nextSibling);
16539          }else if(s.parentNode){
16540             var newS = null;
16541             s.parentNode.bubble(function(){
16542                 if(this.nextSibling){
16543                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16544                     return false;
16545                 }
16546             });
16547             return newS;
16548          }
16549         return null;
16550     },
16551
16552     onKeyDown : function(e){
16553         var s = this.selNode || this.lastSelNode;
16554         // undesirable, but required
16555         var sm = this;
16556         if(!s){
16557             return;
16558         }
16559         var k = e.getKey();
16560         switch(k){
16561              case e.DOWN:
16562                  e.stopEvent();
16563                  this.selectNext();
16564              break;
16565              case e.UP:
16566                  e.stopEvent();
16567                  this.selectPrevious();
16568              break;
16569              case e.RIGHT:
16570                  e.preventDefault();
16571                  if(s.hasChildNodes()){
16572                      if(!s.isExpanded()){
16573                          s.expand();
16574                      }else if(s.firstChild){
16575                          this.select(s.firstChild, e);
16576                      }
16577                  }
16578              break;
16579              case e.LEFT:
16580                  e.preventDefault();
16581                  if(s.hasChildNodes() && s.isExpanded()){
16582                      s.collapse();
16583                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16584                      this.select(s.parentNode, e);
16585                  }
16586              break;
16587         };
16588     }
16589 });
16590
16591 /**
16592  * @class Roo.tree.MultiSelectionModel
16593  * @extends Roo.util.Observable
16594  * Multi selection for a TreePanel.
16595  * @param {Object} cfg Configuration
16596  */
16597 Roo.tree.MultiSelectionModel = function(){
16598    this.selNodes = [];
16599    this.selMap = {};
16600    this.addEvents({
16601        /**
16602         * @event selectionchange
16603         * Fires when the selected nodes change
16604         * @param {MultiSelectionModel} this
16605         * @param {Array} nodes Array of the selected nodes
16606         */
16607        "selectionchange" : true
16608    });
16609    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16610    
16611 };
16612
16613 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16614     init : function(tree){
16615         this.tree = tree;
16616         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16617         tree.on("click", this.onNodeClick, this);
16618     },
16619     
16620     onNodeClick : function(node, e){
16621         this.select(node, e, e.ctrlKey);
16622     },
16623     
16624     /**
16625      * Select a node.
16626      * @param {TreeNode} node The node to select
16627      * @param {EventObject} e (optional) An event associated with the selection
16628      * @param {Boolean} keepExisting True to retain existing selections
16629      * @return {TreeNode} The selected node
16630      */
16631     select : function(node, e, keepExisting){
16632         if(keepExisting !== true){
16633             this.clearSelections(true);
16634         }
16635         if(this.isSelected(node)){
16636             this.lastSelNode = node;
16637             return node;
16638         }
16639         this.selNodes.push(node);
16640         this.selMap[node.id] = node;
16641         this.lastSelNode = node;
16642         node.ui.onSelectedChange(true);
16643         this.fireEvent("selectionchange", this, this.selNodes);
16644         return node;
16645     },
16646     
16647     /**
16648      * Deselect a node.
16649      * @param {TreeNode} node The node to unselect
16650      */
16651     unselect : function(node){
16652         if(this.selMap[node.id]){
16653             node.ui.onSelectedChange(false);
16654             var sn = this.selNodes;
16655             var index = -1;
16656             if(sn.indexOf){
16657                 index = sn.indexOf(node);
16658             }else{
16659                 for(var i = 0, len = sn.length; i < len; i++){
16660                     if(sn[i] == node){
16661                         index = i;
16662                         break;
16663                     }
16664                 }
16665             }
16666             if(index != -1){
16667                 this.selNodes.splice(index, 1);
16668             }
16669             delete this.selMap[node.id];
16670             this.fireEvent("selectionchange", this, this.selNodes);
16671         }
16672     },
16673     
16674     /**
16675      * Clear all selections
16676      */
16677     clearSelections : function(suppressEvent){
16678         var sn = this.selNodes;
16679         if(sn.length > 0){
16680             for(var i = 0, len = sn.length; i < len; i++){
16681                 sn[i].ui.onSelectedChange(false);
16682             }
16683             this.selNodes = [];
16684             this.selMap = {};
16685             if(suppressEvent !== true){
16686                 this.fireEvent("selectionchange", this, this.selNodes);
16687             }
16688         }
16689     },
16690     
16691     /**
16692      * Returns true if the node is selected
16693      * @param {TreeNode} node The node to check
16694      * @return {Boolean}
16695      */
16696     isSelected : function(node){
16697         return this.selMap[node.id] ? true : false;  
16698     },
16699     
16700     /**
16701      * Returns an array of the selected nodes
16702      * @return {Array}
16703      */
16704     getSelectedNodes : function(){
16705         return this.selNodes;    
16706     },
16707
16708     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16709
16710     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16711
16712     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16713 });/*
16714  * Based on:
16715  * Ext JS Library 1.1.1
16716  * Copyright(c) 2006-2007, Ext JS, LLC.
16717  *
16718  * Originally Released Under LGPL - original licence link has changed is not relivant.
16719  *
16720  * Fork - LGPL
16721  * <script type="text/javascript">
16722  */
16723  
16724 /**
16725  * @class Roo.tree.TreeNode
16726  * @extends Roo.data.Node
16727  * @cfg {String} text The text for this node
16728  * @cfg {Boolean} expanded true to start the node expanded
16729  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16730  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16731  * @cfg {Boolean} disabled true to start the node disabled
16732  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16733  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16734  * @cfg {String} cls A css class to be added to the node
16735  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16736  * @cfg {String} href URL of the link used for the node (defaults to #)
16737  * @cfg {String} hrefTarget target frame for the link
16738  * @cfg {String} qtip An Ext QuickTip for the node
16739  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16740  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16741  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16742  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16743  * (defaults to undefined with no checkbox rendered)
16744  * @constructor
16745  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16746  */
16747 Roo.tree.TreeNode = function(attributes){
16748     attributes = attributes || {};
16749     if(typeof attributes == "string"){
16750         attributes = {text: attributes};
16751     }
16752     this.childrenRendered = false;
16753     this.rendered = false;
16754     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16755     this.expanded = attributes.expanded === true;
16756     this.isTarget = attributes.isTarget !== false;
16757     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16758     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16759
16760     /**
16761      * Read-only. The text for this node. To change it use setText().
16762      * @type String
16763      */
16764     this.text = attributes.text;
16765     /**
16766      * True if this node is disabled.
16767      * @type Boolean
16768      */
16769     this.disabled = attributes.disabled === true;
16770
16771     this.addEvents({
16772         /**
16773         * @event textchange
16774         * Fires when the text for this node is changed
16775         * @param {Node} this This node
16776         * @param {String} text The new text
16777         * @param {String} oldText The old text
16778         */
16779         "textchange" : true,
16780         /**
16781         * @event beforeexpand
16782         * Fires before this node is expanded, return false to cancel.
16783         * @param {Node} this This node
16784         * @param {Boolean} deep
16785         * @param {Boolean} anim
16786         */
16787         "beforeexpand" : true,
16788         /**
16789         * @event beforecollapse
16790         * Fires before this node is collapsed, return false to cancel.
16791         * @param {Node} this This node
16792         * @param {Boolean} deep
16793         * @param {Boolean} anim
16794         */
16795         "beforecollapse" : true,
16796         /**
16797         * @event expand
16798         * Fires when this node is expanded
16799         * @param {Node} this This node
16800         */
16801         "expand" : true,
16802         /**
16803         * @event disabledchange
16804         * Fires when the disabled status of this node changes
16805         * @param {Node} this This node
16806         * @param {Boolean} disabled
16807         */
16808         "disabledchange" : true,
16809         /**
16810         * @event collapse
16811         * Fires when this node is collapsed
16812         * @param {Node} this This node
16813         */
16814         "collapse" : true,
16815         /**
16816         * @event beforeclick
16817         * Fires before click processing. Return false to cancel the default action.
16818         * @param {Node} this This node
16819         * @param {Roo.EventObject} e The event object
16820         */
16821         "beforeclick":true,
16822         /**
16823         * @event checkchange
16824         * Fires when a node with a checkbox's checked property changes
16825         * @param {Node} this This node
16826         * @param {Boolean} checked
16827         */
16828         "checkchange":true,
16829         /**
16830         * @event click
16831         * Fires when this node is clicked
16832         * @param {Node} this This node
16833         * @param {Roo.EventObject} e The event object
16834         */
16835         "click":true,
16836         /**
16837         * @event dblclick
16838         * Fires when this node is double clicked
16839         * @param {Node} this This node
16840         * @param {Roo.EventObject} e The event object
16841         */
16842         "dblclick":true,
16843         /**
16844         * @event contextmenu
16845         * Fires when this node is right clicked
16846         * @param {Node} this This node
16847         * @param {Roo.EventObject} e The event object
16848         */
16849         "contextmenu":true,
16850         /**
16851         * @event beforechildrenrendered
16852         * Fires right before the child nodes for this node are rendered
16853         * @param {Node} this This node
16854         */
16855         "beforechildrenrendered":true
16856     });
16857
16858     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16859
16860     /**
16861      * Read-only. The UI for this node
16862      * @type TreeNodeUI
16863      */
16864     this.ui = new uiClass(this);
16865     
16866     // finally support items[]
16867     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16868         return;
16869     }
16870     
16871     
16872     Roo.each(this.attributes.items, function(c) {
16873         this.appendChild(Roo.factory(c,Roo.Tree));
16874     }, this);
16875     delete this.attributes.items;
16876     
16877     
16878     
16879 };
16880 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16881     preventHScroll: true,
16882     /**
16883      * Returns true if this node is expanded
16884      * @return {Boolean}
16885      */
16886     isExpanded : function(){
16887         return this.expanded;
16888     },
16889
16890     /**
16891      * Returns the UI object for this node
16892      * @return {TreeNodeUI}
16893      */
16894     getUI : function(){
16895         return this.ui;
16896     },
16897
16898     // private override
16899     setFirstChild : function(node){
16900         var of = this.firstChild;
16901         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16902         if(this.childrenRendered && of && node != of){
16903             of.renderIndent(true, true);
16904         }
16905         if(this.rendered){
16906             this.renderIndent(true, true);
16907         }
16908     },
16909
16910     // private override
16911     setLastChild : function(node){
16912         var ol = this.lastChild;
16913         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16914         if(this.childrenRendered && ol && node != ol){
16915             ol.renderIndent(true, true);
16916         }
16917         if(this.rendered){
16918             this.renderIndent(true, true);
16919         }
16920     },
16921
16922     // these methods are overridden to provide lazy rendering support
16923     // private override
16924     appendChild : function()
16925     {
16926         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16927         if(node && this.childrenRendered){
16928             node.render();
16929         }
16930         this.ui.updateExpandIcon();
16931         return node;
16932     },
16933
16934     // private override
16935     removeChild : function(node){
16936         this.ownerTree.getSelectionModel().unselect(node);
16937         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16938         // if it's been rendered remove dom node
16939         if(this.childrenRendered){
16940             node.ui.remove();
16941         }
16942         if(this.childNodes.length < 1){
16943             this.collapse(false, false);
16944         }else{
16945             this.ui.updateExpandIcon();
16946         }
16947         if(!this.firstChild) {
16948             this.childrenRendered = false;
16949         }
16950         return node;
16951     },
16952
16953     // private override
16954     insertBefore : function(node, refNode){
16955         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16956         if(newNode && refNode && this.childrenRendered){
16957             node.render();
16958         }
16959         this.ui.updateExpandIcon();
16960         return newNode;
16961     },
16962
16963     /**
16964      * Sets the text for this node
16965      * @param {String} text
16966      */
16967     setText : function(text){
16968         var oldText = this.text;
16969         this.text = text;
16970         this.attributes.text = text;
16971         if(this.rendered){ // event without subscribing
16972             this.ui.onTextChange(this, text, oldText);
16973         }
16974         this.fireEvent("textchange", this, text, oldText);
16975     },
16976
16977     /**
16978      * Triggers selection of this node
16979      */
16980     select : function(){
16981         this.getOwnerTree().getSelectionModel().select(this);
16982     },
16983
16984     /**
16985      * Triggers deselection of this node
16986      */
16987     unselect : function(){
16988         this.getOwnerTree().getSelectionModel().unselect(this);
16989     },
16990
16991     /**
16992      * Returns true if this node is selected
16993      * @return {Boolean}
16994      */
16995     isSelected : function(){
16996         return this.getOwnerTree().getSelectionModel().isSelected(this);
16997     },
16998
16999     /**
17000      * Expand this node.
17001      * @param {Boolean} deep (optional) True to expand all children as well
17002      * @param {Boolean} anim (optional) false to cancel the default animation
17003      * @param {Function} callback (optional) A callback to be called when
17004      * expanding this node completes (does not wait for deep expand to complete).
17005      * Called with 1 parameter, this node.
17006      */
17007     expand : function(deep, anim, callback){
17008         if(!this.expanded){
17009             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17010                 return;
17011             }
17012             if(!this.childrenRendered){
17013                 this.renderChildren();
17014             }
17015             this.expanded = true;
17016             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17017                 this.ui.animExpand(function(){
17018                     this.fireEvent("expand", this);
17019                     if(typeof callback == "function"){
17020                         callback(this);
17021                     }
17022                     if(deep === true){
17023                         this.expandChildNodes(true);
17024                     }
17025                 }.createDelegate(this));
17026                 return;
17027             }else{
17028                 this.ui.expand();
17029                 this.fireEvent("expand", this);
17030                 if(typeof callback == "function"){
17031                     callback(this);
17032                 }
17033             }
17034         }else{
17035            if(typeof callback == "function"){
17036                callback(this);
17037            }
17038         }
17039         if(deep === true){
17040             this.expandChildNodes(true);
17041         }
17042     },
17043
17044     isHiddenRoot : function(){
17045         return this.isRoot && !this.getOwnerTree().rootVisible;
17046     },
17047
17048     /**
17049      * Collapse this node.
17050      * @param {Boolean} deep (optional) True to collapse all children as well
17051      * @param {Boolean} anim (optional) false to cancel the default animation
17052      */
17053     collapse : function(deep, anim){
17054         if(this.expanded && !this.isHiddenRoot()){
17055             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17056                 return;
17057             }
17058             this.expanded = false;
17059             if((this.getOwnerTree().animate && anim !== false) || anim){
17060                 this.ui.animCollapse(function(){
17061                     this.fireEvent("collapse", this);
17062                     if(deep === true){
17063                         this.collapseChildNodes(true);
17064                     }
17065                 }.createDelegate(this));
17066                 return;
17067             }else{
17068                 this.ui.collapse();
17069                 this.fireEvent("collapse", this);
17070             }
17071         }
17072         if(deep === true){
17073             var cs = this.childNodes;
17074             for(var i = 0, len = cs.length; i < len; i++) {
17075                 cs[i].collapse(true, false);
17076             }
17077         }
17078     },
17079
17080     // private
17081     delayedExpand : function(delay){
17082         if(!this.expandProcId){
17083             this.expandProcId = this.expand.defer(delay, this);
17084         }
17085     },
17086
17087     // private
17088     cancelExpand : function(){
17089         if(this.expandProcId){
17090             clearTimeout(this.expandProcId);
17091         }
17092         this.expandProcId = false;
17093     },
17094
17095     /**
17096      * Toggles expanded/collapsed state of the node
17097      */
17098     toggle : function(){
17099         if(this.expanded){
17100             this.collapse();
17101         }else{
17102             this.expand();
17103         }
17104     },
17105
17106     /**
17107      * Ensures all parent nodes are expanded
17108      */
17109     ensureVisible : function(callback){
17110         var tree = this.getOwnerTree();
17111         tree.expandPath(this.parentNode.getPath(), false, function(){
17112             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17113             Roo.callback(callback);
17114         }.createDelegate(this));
17115     },
17116
17117     /**
17118      * Expand all child nodes
17119      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17120      */
17121     expandChildNodes : function(deep){
17122         var cs = this.childNodes;
17123         for(var i = 0, len = cs.length; i < len; i++) {
17124                 cs[i].expand(deep);
17125         }
17126     },
17127
17128     /**
17129      * Collapse all child nodes
17130      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17131      */
17132     collapseChildNodes : function(deep){
17133         var cs = this.childNodes;
17134         for(var i = 0, len = cs.length; i < len; i++) {
17135                 cs[i].collapse(deep);
17136         }
17137     },
17138
17139     /**
17140      * Disables this node
17141      */
17142     disable : function(){
17143         this.disabled = true;
17144         this.unselect();
17145         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17146             this.ui.onDisableChange(this, true);
17147         }
17148         this.fireEvent("disabledchange", this, true);
17149     },
17150
17151     /**
17152      * Enables this node
17153      */
17154     enable : function(){
17155         this.disabled = false;
17156         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17157             this.ui.onDisableChange(this, false);
17158         }
17159         this.fireEvent("disabledchange", this, false);
17160     },
17161
17162     // private
17163     renderChildren : function(suppressEvent){
17164         if(suppressEvent !== false){
17165             this.fireEvent("beforechildrenrendered", this);
17166         }
17167         var cs = this.childNodes;
17168         for(var i = 0, len = cs.length; i < len; i++){
17169             cs[i].render(true);
17170         }
17171         this.childrenRendered = true;
17172     },
17173
17174     // private
17175     sort : function(fn, scope){
17176         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17177         if(this.childrenRendered){
17178             var cs = this.childNodes;
17179             for(var i = 0, len = cs.length; i < len; i++){
17180                 cs[i].render(true);
17181             }
17182         }
17183     },
17184
17185     // private
17186     render : function(bulkRender){
17187         this.ui.render(bulkRender);
17188         if(!this.rendered){
17189             this.rendered = true;
17190             if(this.expanded){
17191                 this.expanded = false;
17192                 this.expand(false, false);
17193             }
17194         }
17195     },
17196
17197     // private
17198     renderIndent : function(deep, refresh){
17199         if(refresh){
17200             this.ui.childIndent = null;
17201         }
17202         this.ui.renderIndent();
17203         if(deep === true && this.childrenRendered){
17204             var cs = this.childNodes;
17205             for(var i = 0, len = cs.length; i < len; i++){
17206                 cs[i].renderIndent(true, refresh);
17207             }
17208         }
17209     }
17210 });/*
17211  * Based on:
17212  * Ext JS Library 1.1.1
17213  * Copyright(c) 2006-2007, Ext JS, LLC.
17214  *
17215  * Originally Released Under LGPL - original licence link has changed is not relivant.
17216  *
17217  * Fork - LGPL
17218  * <script type="text/javascript">
17219  */
17220  
17221 /**
17222  * @class Roo.tree.AsyncTreeNode
17223  * @extends Roo.tree.TreeNode
17224  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17225  * @constructor
17226  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17227  */
17228  Roo.tree.AsyncTreeNode = function(config){
17229     this.loaded = false;
17230     this.loading = false;
17231     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17232     /**
17233     * @event beforeload
17234     * Fires before this node is loaded, return false to cancel
17235     * @param {Node} this This node
17236     */
17237     this.addEvents({'beforeload':true, 'load': true});
17238     /**
17239     * @event load
17240     * Fires when this node is loaded
17241     * @param {Node} this This node
17242     */
17243     /**
17244      * The loader used by this node (defaults to using the tree's defined loader)
17245      * @type TreeLoader
17246      * @property loader
17247      */
17248 };
17249 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17250     expand : function(deep, anim, callback){
17251         if(this.loading){ // if an async load is already running, waiting til it's done
17252             var timer;
17253             var f = function(){
17254                 if(!this.loading){ // done loading
17255                     clearInterval(timer);
17256                     this.expand(deep, anim, callback);
17257                 }
17258             }.createDelegate(this);
17259             timer = setInterval(f, 200);
17260             return;
17261         }
17262         if(!this.loaded){
17263             if(this.fireEvent("beforeload", this) === false){
17264                 return;
17265             }
17266             this.loading = true;
17267             this.ui.beforeLoad(this);
17268             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17269             if(loader){
17270                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17271                 return;
17272             }
17273         }
17274         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17275     },
17276     
17277     /**
17278      * Returns true if this node is currently loading
17279      * @return {Boolean}
17280      */
17281     isLoading : function(){
17282         return this.loading;  
17283     },
17284     
17285     loadComplete : function(deep, anim, callback){
17286         this.loading = false;
17287         this.loaded = true;
17288         this.ui.afterLoad(this);
17289         this.fireEvent("load", this);
17290         this.expand(deep, anim, callback);
17291     },
17292     
17293     /**
17294      * Returns true if this node has been loaded
17295      * @return {Boolean}
17296      */
17297     isLoaded : function(){
17298         return this.loaded;
17299     },
17300     
17301     hasChildNodes : function(){
17302         if(!this.isLeaf() && !this.loaded){
17303             return true;
17304         }else{
17305             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17306         }
17307     },
17308
17309     /**
17310      * Trigger a reload for this node
17311      * @param {Function} callback
17312      */
17313     reload : function(callback){
17314         this.collapse(false, false);
17315         while(this.firstChild){
17316             this.removeChild(this.firstChild);
17317         }
17318         this.childrenRendered = false;
17319         this.loaded = false;
17320         if(this.isHiddenRoot()){
17321             this.expanded = false;
17322         }
17323         this.expand(false, false, callback);
17324     }
17325 });/*
17326  * Based on:
17327  * Ext JS Library 1.1.1
17328  * Copyright(c) 2006-2007, Ext JS, LLC.
17329  *
17330  * Originally Released Under LGPL - original licence link has changed is not relivant.
17331  *
17332  * Fork - LGPL
17333  * <script type="text/javascript">
17334  */
17335  
17336 /**
17337  * @class Roo.tree.TreeNodeUI
17338  * @constructor
17339  * @param {Object} node The node to render
17340  * The TreeNode UI implementation is separate from the
17341  * tree implementation. Unless you are customizing the tree UI,
17342  * you should never have to use this directly.
17343  */
17344 Roo.tree.TreeNodeUI = function(node){
17345     this.node = node;
17346     this.rendered = false;
17347     this.animating = false;
17348     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17349 };
17350
17351 Roo.tree.TreeNodeUI.prototype = {
17352     removeChild : function(node){
17353         if(this.rendered){
17354             this.ctNode.removeChild(node.ui.getEl());
17355         }
17356     },
17357
17358     beforeLoad : function(){
17359          this.addClass("x-tree-node-loading");
17360     },
17361
17362     afterLoad : function(){
17363          this.removeClass("x-tree-node-loading");
17364     },
17365
17366     onTextChange : function(node, text, oldText){
17367         if(this.rendered){
17368             this.textNode.innerHTML = text;
17369         }
17370     },
17371
17372     onDisableChange : function(node, state){
17373         this.disabled = state;
17374         if(state){
17375             this.addClass("x-tree-node-disabled");
17376         }else{
17377             this.removeClass("x-tree-node-disabled");
17378         }
17379     },
17380
17381     onSelectedChange : function(state){
17382         if(state){
17383             this.focus();
17384             this.addClass("x-tree-selected");
17385         }else{
17386             //this.blur();
17387             this.removeClass("x-tree-selected");
17388         }
17389     },
17390
17391     onMove : function(tree, node, oldParent, newParent, index, refNode){
17392         this.childIndent = null;
17393         if(this.rendered){
17394             var targetNode = newParent.ui.getContainer();
17395             if(!targetNode){//target not rendered
17396                 this.holder = document.createElement("div");
17397                 this.holder.appendChild(this.wrap);
17398                 return;
17399             }
17400             var insertBefore = refNode ? refNode.ui.getEl() : null;
17401             if(insertBefore){
17402                 targetNode.insertBefore(this.wrap, insertBefore);
17403             }else{
17404                 targetNode.appendChild(this.wrap);
17405             }
17406             this.node.renderIndent(true);
17407         }
17408     },
17409
17410     addClass : function(cls){
17411         if(this.elNode){
17412             Roo.fly(this.elNode).addClass(cls);
17413         }
17414     },
17415
17416     removeClass : function(cls){
17417         if(this.elNode){
17418             Roo.fly(this.elNode).removeClass(cls);
17419         }
17420     },
17421
17422     remove : function(){
17423         if(this.rendered){
17424             this.holder = document.createElement("div");
17425             this.holder.appendChild(this.wrap);
17426         }
17427     },
17428
17429     fireEvent : function(){
17430         return this.node.fireEvent.apply(this.node, arguments);
17431     },
17432
17433     initEvents : function(){
17434         this.node.on("move", this.onMove, this);
17435         var E = Roo.EventManager;
17436         var a = this.anchor;
17437
17438         var el = Roo.fly(a, '_treeui');
17439
17440         if(Roo.isOpera){ // opera render bug ignores the CSS
17441             el.setStyle("text-decoration", "none");
17442         }
17443
17444         el.on("click", this.onClick, this);
17445         el.on("dblclick", this.onDblClick, this);
17446
17447         if(this.checkbox){
17448             Roo.EventManager.on(this.checkbox,
17449                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17450         }
17451
17452         el.on("contextmenu", this.onContextMenu, this);
17453
17454         var icon = Roo.fly(this.iconNode);
17455         icon.on("click", this.onClick, this);
17456         icon.on("dblclick", this.onDblClick, this);
17457         icon.on("contextmenu", this.onContextMenu, this);
17458         E.on(this.ecNode, "click", this.ecClick, this, true);
17459
17460         if(this.node.disabled){
17461             this.addClass("x-tree-node-disabled");
17462         }
17463         if(this.node.hidden){
17464             this.addClass("x-tree-node-disabled");
17465         }
17466         var ot = this.node.getOwnerTree();
17467         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17468         if(dd && (!this.node.isRoot || ot.rootVisible)){
17469             Roo.dd.Registry.register(this.elNode, {
17470                 node: this.node,
17471                 handles: this.getDDHandles(),
17472                 isHandle: false
17473             });
17474         }
17475     },
17476
17477     getDDHandles : function(){
17478         return [this.iconNode, this.textNode];
17479     },
17480
17481     hide : function(){
17482         if(this.rendered){
17483             this.wrap.style.display = "none";
17484         }
17485     },
17486
17487     show : function(){
17488         if(this.rendered){
17489             this.wrap.style.display = "";
17490         }
17491     },
17492
17493     onContextMenu : function(e){
17494         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17495             e.preventDefault();
17496             this.focus();
17497             this.fireEvent("contextmenu", this.node, e);
17498         }
17499     },
17500
17501     onClick : function(e){
17502         if(this.dropping){
17503             e.stopEvent();
17504             return;
17505         }
17506         if(this.fireEvent("beforeclick", this.node, e) !== false){
17507             if(!this.disabled && this.node.attributes.href){
17508                 this.fireEvent("click", this.node, e);
17509                 return;
17510             }
17511             e.preventDefault();
17512             if(this.disabled){
17513                 return;
17514             }
17515
17516             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17517                 this.node.toggle();
17518             }
17519
17520             this.fireEvent("click", this.node, e);
17521         }else{
17522             e.stopEvent();
17523         }
17524     },
17525
17526     onDblClick : function(e){
17527         e.preventDefault();
17528         if(this.disabled){
17529             return;
17530         }
17531         if(this.checkbox){
17532             this.toggleCheck();
17533         }
17534         if(!this.animating && this.node.hasChildNodes()){
17535             this.node.toggle();
17536         }
17537         this.fireEvent("dblclick", this.node, e);
17538     },
17539
17540     onCheckChange : function(){
17541         var checked = this.checkbox.checked;
17542         this.node.attributes.checked = checked;
17543         this.fireEvent('checkchange', this.node, checked);
17544     },
17545
17546     ecClick : function(e){
17547         if(!this.animating && this.node.hasChildNodes()){
17548             this.node.toggle();
17549         }
17550     },
17551
17552     startDrop : function(){
17553         this.dropping = true;
17554     },
17555
17556     // delayed drop so the click event doesn't get fired on a drop
17557     endDrop : function(){
17558        setTimeout(function(){
17559            this.dropping = false;
17560        }.createDelegate(this), 50);
17561     },
17562
17563     expand : function(){
17564         this.updateExpandIcon();
17565         this.ctNode.style.display = "";
17566     },
17567
17568     focus : function(){
17569         if(!this.node.preventHScroll){
17570             try{this.anchor.focus();
17571             }catch(e){}
17572         }else if(!Roo.isIE){
17573             try{
17574                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17575                 var l = noscroll.scrollLeft;
17576                 this.anchor.focus();
17577                 noscroll.scrollLeft = l;
17578             }catch(e){}
17579         }
17580     },
17581
17582     toggleCheck : function(value){
17583         var cb = this.checkbox;
17584         if(cb){
17585             cb.checked = (value === undefined ? !cb.checked : value);
17586         }
17587     },
17588
17589     blur : function(){
17590         try{
17591             this.anchor.blur();
17592         }catch(e){}
17593     },
17594
17595     animExpand : function(callback){
17596         var ct = Roo.get(this.ctNode);
17597         ct.stopFx();
17598         if(!this.node.hasChildNodes()){
17599             this.updateExpandIcon();
17600             this.ctNode.style.display = "";
17601             Roo.callback(callback);
17602             return;
17603         }
17604         this.animating = true;
17605         this.updateExpandIcon();
17606
17607         ct.slideIn('t', {
17608            callback : function(){
17609                this.animating = false;
17610                Roo.callback(callback);
17611             },
17612             scope: this,
17613             duration: this.node.ownerTree.duration || .25
17614         });
17615     },
17616
17617     highlight : function(){
17618         var tree = this.node.getOwnerTree();
17619         Roo.fly(this.wrap).highlight(
17620             tree.hlColor || "C3DAF9",
17621             {endColor: tree.hlBaseColor}
17622         );
17623     },
17624
17625     collapse : function(){
17626         this.updateExpandIcon();
17627         this.ctNode.style.display = "none";
17628     },
17629
17630     animCollapse : function(callback){
17631         var ct = Roo.get(this.ctNode);
17632         ct.enableDisplayMode('block');
17633         ct.stopFx();
17634
17635         this.animating = true;
17636         this.updateExpandIcon();
17637
17638         ct.slideOut('t', {
17639             callback : function(){
17640                this.animating = false;
17641                Roo.callback(callback);
17642             },
17643             scope: this,
17644             duration: this.node.ownerTree.duration || .25
17645         });
17646     },
17647
17648     getContainer : function(){
17649         return this.ctNode;
17650     },
17651
17652     getEl : function(){
17653         return this.wrap;
17654     },
17655
17656     appendDDGhost : function(ghostNode){
17657         ghostNode.appendChild(this.elNode.cloneNode(true));
17658     },
17659
17660     getDDRepairXY : function(){
17661         return Roo.lib.Dom.getXY(this.iconNode);
17662     },
17663
17664     onRender : function(){
17665         this.render();
17666     },
17667
17668     render : function(bulkRender){
17669         var n = this.node, a = n.attributes;
17670         var targetNode = n.parentNode ?
17671               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17672
17673         if(!this.rendered){
17674             this.rendered = true;
17675
17676             this.renderElements(n, a, targetNode, bulkRender);
17677
17678             if(a.qtip){
17679                if(this.textNode.setAttributeNS){
17680                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17681                    if(a.qtipTitle){
17682                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17683                    }
17684                }else{
17685                    this.textNode.setAttribute("ext:qtip", a.qtip);
17686                    if(a.qtipTitle){
17687                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17688                    }
17689                }
17690             }else if(a.qtipCfg){
17691                 a.qtipCfg.target = Roo.id(this.textNode);
17692                 Roo.QuickTips.register(a.qtipCfg);
17693             }
17694             this.initEvents();
17695             if(!this.node.expanded){
17696                 this.updateExpandIcon();
17697             }
17698         }else{
17699             if(bulkRender === true) {
17700                 targetNode.appendChild(this.wrap);
17701             }
17702         }
17703     },
17704
17705     renderElements : function(n, a, targetNode, bulkRender)
17706     {
17707         // add some indent caching, this helps performance when rendering a large tree
17708         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17709         var t = n.getOwnerTree();
17710         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17711         if (typeof(n.attributes.html) != 'undefined') {
17712             txt = n.attributes.html;
17713         }
17714         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17715         var cb = typeof a.checked == 'boolean';
17716         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17717         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17718             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17719             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17720             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17721             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17722             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17723              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17724                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17725             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17726             "</li>"];
17727
17728         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17729             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17730                                 n.nextSibling.ui.getEl(), buf.join(""));
17731         }else{
17732             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17733         }
17734
17735         this.elNode = this.wrap.childNodes[0];
17736         this.ctNode = this.wrap.childNodes[1];
17737         var cs = this.elNode.childNodes;
17738         this.indentNode = cs[0];
17739         this.ecNode = cs[1];
17740         this.iconNode = cs[2];
17741         var index = 3;
17742         if(cb){
17743             this.checkbox = cs[3];
17744             index++;
17745         }
17746         this.anchor = cs[index];
17747         this.textNode = cs[index].firstChild;
17748     },
17749
17750     getAnchor : function(){
17751         return this.anchor;
17752     },
17753
17754     getTextEl : function(){
17755         return this.textNode;
17756     },
17757
17758     getIconEl : function(){
17759         return this.iconNode;
17760     },
17761
17762     isChecked : function(){
17763         return this.checkbox ? this.checkbox.checked : false;
17764     },
17765
17766     updateExpandIcon : function(){
17767         if(this.rendered){
17768             var n = this.node, c1, c2;
17769             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17770             var hasChild = n.hasChildNodes();
17771             if(hasChild){
17772                 if(n.expanded){
17773                     cls += "-minus";
17774                     c1 = "x-tree-node-collapsed";
17775                     c2 = "x-tree-node-expanded";
17776                 }else{
17777                     cls += "-plus";
17778                     c1 = "x-tree-node-expanded";
17779                     c2 = "x-tree-node-collapsed";
17780                 }
17781                 if(this.wasLeaf){
17782                     this.removeClass("x-tree-node-leaf");
17783                     this.wasLeaf = false;
17784                 }
17785                 if(this.c1 != c1 || this.c2 != c2){
17786                     Roo.fly(this.elNode).replaceClass(c1, c2);
17787                     this.c1 = c1; this.c2 = c2;
17788                 }
17789             }else{
17790                 // this changes non-leafs into leafs if they have no children.
17791                 // it's not very rational behaviour..
17792                 
17793                 if(!this.wasLeaf && this.node.leaf){
17794                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17795                     delete this.c1;
17796                     delete this.c2;
17797                     this.wasLeaf = true;
17798                 }
17799             }
17800             var ecc = "x-tree-ec-icon "+cls;
17801             if(this.ecc != ecc){
17802                 this.ecNode.className = ecc;
17803                 this.ecc = ecc;
17804             }
17805         }
17806     },
17807
17808     getChildIndent : function(){
17809         if(!this.childIndent){
17810             var buf = [];
17811             var p = this.node;
17812             while(p){
17813                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17814                     if(!p.isLast()) {
17815                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17816                     } else {
17817                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17818                     }
17819                 }
17820                 p = p.parentNode;
17821             }
17822             this.childIndent = buf.join("");
17823         }
17824         return this.childIndent;
17825     },
17826
17827     renderIndent : function(){
17828         if(this.rendered){
17829             var indent = "";
17830             var p = this.node.parentNode;
17831             if(p){
17832                 indent = p.ui.getChildIndent();
17833             }
17834             if(this.indentMarkup != indent){ // don't rerender if not required
17835                 this.indentNode.innerHTML = indent;
17836                 this.indentMarkup = indent;
17837             }
17838             this.updateExpandIcon();
17839         }
17840     }
17841 };
17842
17843 Roo.tree.RootTreeNodeUI = function(){
17844     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17845 };
17846 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17847     render : function(){
17848         if(!this.rendered){
17849             var targetNode = this.node.ownerTree.innerCt.dom;
17850             this.node.expanded = true;
17851             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17852             this.wrap = this.ctNode = targetNode.firstChild;
17853         }
17854     },
17855     collapse : function(){
17856     },
17857     expand : function(){
17858     }
17859 });/*
17860  * Based on:
17861  * Ext JS Library 1.1.1
17862  * Copyright(c) 2006-2007, Ext JS, LLC.
17863  *
17864  * Originally Released Under LGPL - original licence link has changed is not relivant.
17865  *
17866  * Fork - LGPL
17867  * <script type="text/javascript">
17868  */
17869 /**
17870  * @class Roo.tree.TreeLoader
17871  * @extends Roo.util.Observable
17872  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17873  * nodes from a specified URL. The response must be a javascript Array definition
17874  * who's elements are node definition objects. eg:
17875  * <pre><code>
17876 {  success : true,
17877    data :      [
17878    
17879     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17880     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17881     ]
17882 }
17883
17884
17885 </code></pre>
17886  * <br><br>
17887  * The old style respose with just an array is still supported, but not recommended.
17888  * <br><br>
17889  *
17890  * A server request is sent, and child nodes are loaded only when a node is expanded.
17891  * The loading node's id is passed to the server under the parameter name "node" to
17892  * enable the server to produce the correct child nodes.
17893  * <br><br>
17894  * To pass extra parameters, an event handler may be attached to the "beforeload"
17895  * event, and the parameters specified in the TreeLoader's baseParams property:
17896  * <pre><code>
17897     myTreeLoader.on("beforeload", function(treeLoader, node) {
17898         this.baseParams.category = node.attributes.category;
17899     }, this);
17900 </code></pre><
17901  * This would pass an HTTP parameter called "category" to the server containing
17902  * the value of the Node's "category" attribute.
17903  * @constructor
17904  * Creates a new Treeloader.
17905  * @param {Object} config A config object containing config properties.
17906  */
17907 Roo.tree.TreeLoader = function(config){
17908     this.baseParams = {};
17909     this.requestMethod = "POST";
17910     Roo.apply(this, config);
17911
17912     this.addEvents({
17913     
17914         /**
17915          * @event beforeload
17916          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17917          * @param {Object} This TreeLoader object.
17918          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17919          * @param {Object} callback The callback function specified in the {@link #load} call.
17920          */
17921         beforeload : true,
17922         /**
17923          * @event load
17924          * Fires when the node has been successfuly loaded.
17925          * @param {Object} This TreeLoader object.
17926          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17927          * @param {Object} response The response object containing the data from the server.
17928          */
17929         load : true,
17930         /**
17931          * @event loadexception
17932          * Fires if the network request failed.
17933          * @param {Object} This TreeLoader object.
17934          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17935          * @param {Object} response The response object containing the data from the server.
17936          */
17937         loadexception : true,
17938         /**
17939          * @event create
17940          * Fires before a node is created, enabling you to return custom Node types 
17941          * @param {Object} This TreeLoader object.
17942          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17943          */
17944         create : true
17945     });
17946
17947     Roo.tree.TreeLoader.superclass.constructor.call(this);
17948 };
17949
17950 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17951     /**
17952     * @cfg {String} dataUrl The URL from which to request a Json string which
17953     * specifies an array of node definition object representing the child nodes
17954     * to be loaded.
17955     */
17956     /**
17957     * @cfg {String} requestMethod either GET or POST
17958     * defaults to POST (due to BC)
17959     * to be loaded.
17960     */
17961     /**
17962     * @cfg {Object} baseParams (optional) An object containing properties which
17963     * specify HTTP parameters to be passed to each request for child nodes.
17964     */
17965     /**
17966     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17967     * created by this loader. If the attributes sent by the server have an attribute in this object,
17968     * they take priority.
17969     */
17970     /**
17971     * @cfg {Object} uiProviders (optional) An object containing properties which
17972     * 
17973     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17974     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17975     * <i>uiProvider</i> attribute of a returned child node is a string rather
17976     * than a reference to a TreeNodeUI implementation, this that string value
17977     * is used as a property name in the uiProviders object. You can define the provider named
17978     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17979     */
17980     uiProviders : {},
17981
17982     /**
17983     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17984     * child nodes before loading.
17985     */
17986     clearOnLoad : true,
17987
17988     /**
17989     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17990     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17991     * Grid query { data : [ .....] }
17992     */
17993     
17994     root : false,
17995      /**
17996     * @cfg {String} queryParam (optional) 
17997     * Name of the query as it will be passed on the querystring (defaults to 'node')
17998     * eg. the request will be ?node=[id]
17999     */
18000     
18001     
18002     queryParam: false,
18003     
18004     /**
18005      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18006      * This is called automatically when a node is expanded, but may be used to reload
18007      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18008      * @param {Roo.tree.TreeNode} node
18009      * @param {Function} callback
18010      */
18011     load : function(node, callback){
18012         if(this.clearOnLoad){
18013             while(node.firstChild){
18014                 node.removeChild(node.firstChild);
18015             }
18016         }
18017         if(node.attributes.children){ // preloaded json children
18018             var cs = node.attributes.children;
18019             for(var i = 0, len = cs.length; i < len; i++){
18020                 node.appendChild(this.createNode(cs[i]));
18021             }
18022             if(typeof callback == "function"){
18023                 callback();
18024             }
18025         }else if(this.dataUrl){
18026             this.requestData(node, callback);
18027         }
18028     },
18029
18030     getParams: function(node){
18031         var buf = [], bp = this.baseParams;
18032         for(var key in bp){
18033             if(typeof bp[key] != "function"){
18034                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18035             }
18036         }
18037         var n = this.queryParam === false ? 'node' : this.queryParam;
18038         buf.push(n + "=", encodeURIComponent(node.id));
18039         return buf.join("");
18040     },
18041
18042     requestData : function(node, callback){
18043         if(this.fireEvent("beforeload", this, node, callback) !== false){
18044             this.transId = Roo.Ajax.request({
18045                 method:this.requestMethod,
18046                 url: this.dataUrl||this.url,
18047                 success: this.handleResponse,
18048                 failure: this.handleFailure,
18049                 scope: this,
18050                 argument: {callback: callback, node: node},
18051                 params: this.getParams(node)
18052             });
18053         }else{
18054             // if the load is cancelled, make sure we notify
18055             // the node that we are done
18056             if(typeof callback == "function"){
18057                 callback();
18058             }
18059         }
18060     },
18061
18062     isLoading : function(){
18063         return this.transId ? true : false;
18064     },
18065
18066     abort : function(){
18067         if(this.isLoading()){
18068             Roo.Ajax.abort(this.transId);
18069         }
18070     },
18071
18072     // private
18073     createNode : function(attr)
18074     {
18075         // apply baseAttrs, nice idea Corey!
18076         if(this.baseAttrs){
18077             Roo.applyIf(attr, this.baseAttrs);
18078         }
18079         if(this.applyLoader !== false){
18080             attr.loader = this;
18081         }
18082         // uiProvider = depreciated..
18083         
18084         if(typeof(attr.uiProvider) == 'string'){
18085            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18086                 /**  eval:var:attr */ eval(attr.uiProvider);
18087         }
18088         if(typeof(this.uiProviders['default']) != 'undefined') {
18089             attr.uiProvider = this.uiProviders['default'];
18090         }
18091         
18092         this.fireEvent('create', this, attr);
18093         
18094         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18095         return(attr.leaf ?
18096                         new Roo.tree.TreeNode(attr) :
18097                         new Roo.tree.AsyncTreeNode(attr));
18098     },
18099
18100     processResponse : function(response, node, callback)
18101     {
18102         var json = response.responseText;
18103         try {
18104             
18105             var o = Roo.decode(json);
18106             
18107             if (this.root === false && typeof(o.success) != undefined) {
18108                 this.root = 'data'; // the default behaviour for list like data..
18109                 }
18110                 
18111             if (this.root !== false &&  !o.success) {
18112                 // it's a failure condition.
18113                 var a = response.argument;
18114                 this.fireEvent("loadexception", this, a.node, response);
18115                 Roo.log("Load failed - should have a handler really");
18116                 return;
18117             }
18118             
18119             
18120             
18121             if (this.root !== false) {
18122                  o = o[this.root];
18123             }
18124             
18125             for(var i = 0, len = o.length; i < len; i++){
18126                 var n = this.createNode(o[i]);
18127                 if(n){
18128                     node.appendChild(n);
18129                 }
18130             }
18131             if(typeof callback == "function"){
18132                 callback(this, node);
18133             }
18134         }catch(e){
18135             this.handleFailure(response);
18136         }
18137     },
18138
18139     handleResponse : function(response){
18140         this.transId = false;
18141         var a = response.argument;
18142         this.processResponse(response, a.node, a.callback);
18143         this.fireEvent("load", this, a.node, response);
18144     },
18145
18146     handleFailure : function(response)
18147     {
18148         // should handle failure better..
18149         this.transId = false;
18150         var a = response.argument;
18151         this.fireEvent("loadexception", this, a.node, response);
18152         if(typeof a.callback == "function"){
18153             a.callback(this, a.node);
18154         }
18155     }
18156 });/*
18157  * Based on:
18158  * Ext JS Library 1.1.1
18159  * Copyright(c) 2006-2007, Ext JS, LLC.
18160  *
18161  * Originally Released Under LGPL - original licence link has changed is not relivant.
18162  *
18163  * Fork - LGPL
18164  * <script type="text/javascript">
18165  */
18166
18167 /**
18168 * @class Roo.tree.TreeFilter
18169 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18170 * @param {TreePanel} tree
18171 * @param {Object} config (optional)
18172  */
18173 Roo.tree.TreeFilter = function(tree, config){
18174     this.tree = tree;
18175     this.filtered = {};
18176     Roo.apply(this, config);
18177 };
18178
18179 Roo.tree.TreeFilter.prototype = {
18180     clearBlank:false,
18181     reverse:false,
18182     autoClear:false,
18183     remove:false,
18184
18185      /**
18186      * Filter the data by a specific attribute.
18187      * @param {String/RegExp} value Either string that the attribute value
18188      * should start with or a RegExp to test against the attribute
18189      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18190      * @param {TreeNode} startNode (optional) The node to start the filter at.
18191      */
18192     filter : function(value, attr, startNode){
18193         attr = attr || "text";
18194         var f;
18195         if(typeof value == "string"){
18196             var vlen = value.length;
18197             // auto clear empty filter
18198             if(vlen == 0 && this.clearBlank){
18199                 this.clear();
18200                 return;
18201             }
18202             value = value.toLowerCase();
18203             f = function(n){
18204                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18205             };
18206         }else if(value.exec){ // regex?
18207             f = function(n){
18208                 return value.test(n.attributes[attr]);
18209             };
18210         }else{
18211             throw 'Illegal filter type, must be string or regex';
18212         }
18213         this.filterBy(f, null, startNode);
18214         },
18215
18216     /**
18217      * Filter by a function. The passed function will be called with each
18218      * node in the tree (or from the startNode). If the function returns true, the node is kept
18219      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18220      * @param {Function} fn The filter function
18221      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18222      */
18223     filterBy : function(fn, scope, startNode){
18224         startNode = startNode || this.tree.root;
18225         if(this.autoClear){
18226             this.clear();
18227         }
18228         var af = this.filtered, rv = this.reverse;
18229         var f = function(n){
18230             if(n == startNode){
18231                 return true;
18232             }
18233             if(af[n.id]){
18234                 return false;
18235             }
18236             var m = fn.call(scope || n, n);
18237             if(!m || rv){
18238                 af[n.id] = n;
18239                 n.ui.hide();
18240                 return false;
18241             }
18242             return true;
18243         };
18244         startNode.cascade(f);
18245         if(this.remove){
18246            for(var id in af){
18247                if(typeof id != "function"){
18248                    var n = af[id];
18249                    if(n && n.parentNode){
18250                        n.parentNode.removeChild(n);
18251                    }
18252                }
18253            }
18254         }
18255     },
18256
18257     /**
18258      * Clears the current filter. Note: with the "remove" option
18259      * set a filter cannot be cleared.
18260      */
18261     clear : function(){
18262         var t = this.tree;
18263         var af = this.filtered;
18264         for(var id in af){
18265             if(typeof id != "function"){
18266                 var n = af[id];
18267                 if(n){
18268                     n.ui.show();
18269                 }
18270             }
18271         }
18272         this.filtered = {};
18273     }
18274 };
18275 /*
18276  * Based on:
18277  * Ext JS Library 1.1.1
18278  * Copyright(c) 2006-2007, Ext JS, LLC.
18279  *
18280  * Originally Released Under LGPL - original licence link has changed is not relivant.
18281  *
18282  * Fork - LGPL
18283  * <script type="text/javascript">
18284  */
18285  
18286
18287 /**
18288  * @class Roo.tree.TreeSorter
18289  * Provides sorting of nodes in a TreePanel
18290  * 
18291  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18292  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18293  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18294  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18295  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18296  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18297  * @constructor
18298  * @param {TreePanel} tree
18299  * @param {Object} config
18300  */
18301 Roo.tree.TreeSorter = function(tree, config){
18302     Roo.apply(this, config);
18303     tree.on("beforechildrenrendered", this.doSort, this);
18304     tree.on("append", this.updateSort, this);
18305     tree.on("insert", this.updateSort, this);
18306     
18307     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18308     var p = this.property || "text";
18309     var sortType = this.sortType;
18310     var fs = this.folderSort;
18311     var cs = this.caseSensitive === true;
18312     var leafAttr = this.leafAttr || 'leaf';
18313
18314     this.sortFn = function(n1, n2){
18315         if(fs){
18316             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18317                 return 1;
18318             }
18319             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18320                 return -1;
18321             }
18322         }
18323         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18324         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18325         if(v1 < v2){
18326                         return dsc ? +1 : -1;
18327                 }else if(v1 > v2){
18328                         return dsc ? -1 : +1;
18329         }else{
18330                 return 0;
18331         }
18332     };
18333 };
18334
18335 Roo.tree.TreeSorter.prototype = {
18336     doSort : function(node){
18337         node.sort(this.sortFn);
18338     },
18339     
18340     compareNodes : function(n1, n2){
18341         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18342     },
18343     
18344     updateSort : function(tree, node){
18345         if(node.childrenRendered){
18346             this.doSort.defer(1, this, [node]);
18347         }
18348     }
18349 };/*
18350  * Based on:
18351  * Ext JS Library 1.1.1
18352  * Copyright(c) 2006-2007, Ext JS, LLC.
18353  *
18354  * Originally Released Under LGPL - original licence link has changed is not relivant.
18355  *
18356  * Fork - LGPL
18357  * <script type="text/javascript">
18358  */
18359
18360 if(Roo.dd.DropZone){
18361     
18362 Roo.tree.TreeDropZone = function(tree, config){
18363     this.allowParentInsert = false;
18364     this.allowContainerDrop = false;
18365     this.appendOnly = false;
18366     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18367     this.tree = tree;
18368     this.lastInsertClass = "x-tree-no-status";
18369     this.dragOverData = {};
18370 };
18371
18372 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18373     ddGroup : "TreeDD",
18374     scroll:  true,
18375     
18376     expandDelay : 1000,
18377     
18378     expandNode : function(node){
18379         if(node.hasChildNodes() && !node.isExpanded()){
18380             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18381         }
18382     },
18383     
18384     queueExpand : function(node){
18385         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18386     },
18387     
18388     cancelExpand : function(){
18389         if(this.expandProcId){
18390             clearTimeout(this.expandProcId);
18391             this.expandProcId = false;
18392         }
18393     },
18394     
18395     isValidDropPoint : function(n, pt, dd, e, data){
18396         if(!n || !data){ return false; }
18397         var targetNode = n.node;
18398         var dropNode = data.node;
18399         // default drop rules
18400         if(!(targetNode && targetNode.isTarget && pt)){
18401             return false;
18402         }
18403         if(pt == "append" && targetNode.allowChildren === false){
18404             return false;
18405         }
18406         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18407             return false;
18408         }
18409         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18410             return false;
18411         }
18412         // reuse the object
18413         var overEvent = this.dragOverData;
18414         overEvent.tree = this.tree;
18415         overEvent.target = targetNode;
18416         overEvent.data = data;
18417         overEvent.point = pt;
18418         overEvent.source = dd;
18419         overEvent.rawEvent = e;
18420         overEvent.dropNode = dropNode;
18421         overEvent.cancel = false;  
18422         var result = this.tree.fireEvent("nodedragover", overEvent);
18423         return overEvent.cancel === false && result !== false;
18424     },
18425     
18426     getDropPoint : function(e, n, dd)
18427     {
18428         var tn = n.node;
18429         if(tn.isRoot){
18430             return tn.allowChildren !== false ? "append" : false; // always append for root
18431         }
18432         var dragEl = n.ddel;
18433         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18434         var y = Roo.lib.Event.getPageY(e);
18435         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18436         
18437         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18438         var noAppend = tn.allowChildren === false;
18439         if(this.appendOnly || tn.parentNode.allowChildren === false){
18440             return noAppend ? false : "append";
18441         }
18442         var noBelow = false;
18443         if(!this.allowParentInsert){
18444             noBelow = tn.hasChildNodes() && tn.isExpanded();
18445         }
18446         var q = (b - t) / (noAppend ? 2 : 3);
18447         if(y >= t && y < (t + q)){
18448             return "above";
18449         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18450             return "below";
18451         }else{
18452             return "append";
18453         }
18454     },
18455     
18456     onNodeEnter : function(n, dd, e, data)
18457     {
18458         this.cancelExpand();
18459     },
18460     
18461     onNodeOver : function(n, dd, e, data)
18462     {
18463        
18464         var pt = this.getDropPoint(e, n, dd);
18465         var node = n.node;
18466         
18467         // auto node expand check
18468         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18469             this.queueExpand(node);
18470         }else if(pt != "append"){
18471             this.cancelExpand();
18472         }
18473         
18474         // set the insert point style on the target node
18475         var returnCls = this.dropNotAllowed;
18476         if(this.isValidDropPoint(n, pt, dd, e, data)){
18477            if(pt){
18478                var el = n.ddel;
18479                var cls;
18480                if(pt == "above"){
18481                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18482                    cls = "x-tree-drag-insert-above";
18483                }else if(pt == "below"){
18484                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18485                    cls = "x-tree-drag-insert-below";
18486                }else{
18487                    returnCls = "x-tree-drop-ok-append";
18488                    cls = "x-tree-drag-append";
18489                }
18490                if(this.lastInsertClass != cls){
18491                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18492                    this.lastInsertClass = cls;
18493                }
18494            }
18495        }
18496        return returnCls;
18497     },
18498     
18499     onNodeOut : function(n, dd, e, data){
18500         
18501         this.cancelExpand();
18502         this.removeDropIndicators(n);
18503     },
18504     
18505     onNodeDrop : function(n, dd, e, data){
18506         var point = this.getDropPoint(e, n, dd);
18507         var targetNode = n.node;
18508         targetNode.ui.startDrop();
18509         if(!this.isValidDropPoint(n, point, dd, e, data)){
18510             targetNode.ui.endDrop();
18511             return false;
18512         }
18513         // first try to find the drop node
18514         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18515         var dropEvent = {
18516             tree : this.tree,
18517             target: targetNode,
18518             data: data,
18519             point: point,
18520             source: dd,
18521             rawEvent: e,
18522             dropNode: dropNode,
18523             cancel: !dropNode   
18524         };
18525         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18526         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18527             targetNode.ui.endDrop();
18528             return false;
18529         }
18530         // allow target changing
18531         targetNode = dropEvent.target;
18532         if(point == "append" && !targetNode.isExpanded()){
18533             targetNode.expand(false, null, function(){
18534                 this.completeDrop(dropEvent);
18535             }.createDelegate(this));
18536         }else{
18537             this.completeDrop(dropEvent);
18538         }
18539         return true;
18540     },
18541     
18542     completeDrop : function(de){
18543         var ns = de.dropNode, p = de.point, t = de.target;
18544         if(!(ns instanceof Array)){
18545             ns = [ns];
18546         }
18547         var n;
18548         for(var i = 0, len = ns.length; i < len; i++){
18549             n = ns[i];
18550             if(p == "above"){
18551                 t.parentNode.insertBefore(n, t);
18552             }else if(p == "below"){
18553                 t.parentNode.insertBefore(n, t.nextSibling);
18554             }else{
18555                 t.appendChild(n);
18556             }
18557         }
18558         n.ui.focus();
18559         if(this.tree.hlDrop){
18560             n.ui.highlight();
18561         }
18562         t.ui.endDrop();
18563         this.tree.fireEvent("nodedrop", de);
18564     },
18565     
18566     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18567         if(this.tree.hlDrop){
18568             dropNode.ui.focus();
18569             dropNode.ui.highlight();
18570         }
18571         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18572     },
18573     
18574     getTree : function(){
18575         return this.tree;
18576     },
18577     
18578     removeDropIndicators : function(n){
18579         if(n && n.ddel){
18580             var el = n.ddel;
18581             Roo.fly(el).removeClass([
18582                     "x-tree-drag-insert-above",
18583                     "x-tree-drag-insert-below",
18584                     "x-tree-drag-append"]);
18585             this.lastInsertClass = "_noclass";
18586         }
18587     },
18588     
18589     beforeDragDrop : function(target, e, id){
18590         this.cancelExpand();
18591         return true;
18592     },
18593     
18594     afterRepair : function(data){
18595         if(data && Roo.enableFx){
18596             data.node.ui.highlight();
18597         }
18598         this.hideProxy();
18599     } 
18600     
18601 });
18602
18603 }
18604 /*
18605  * Based on:
18606  * Ext JS Library 1.1.1
18607  * Copyright(c) 2006-2007, Ext JS, LLC.
18608  *
18609  * Originally Released Under LGPL - original licence link has changed is not relivant.
18610  *
18611  * Fork - LGPL
18612  * <script type="text/javascript">
18613  */
18614  
18615
18616 if(Roo.dd.DragZone){
18617 Roo.tree.TreeDragZone = function(tree, config){
18618     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18619     this.tree = tree;
18620 };
18621
18622 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18623     ddGroup : "TreeDD",
18624    
18625     onBeforeDrag : function(data, e){
18626         var n = data.node;
18627         return n && n.draggable && !n.disabled;
18628     },
18629      
18630     
18631     onInitDrag : function(e){
18632         var data = this.dragData;
18633         this.tree.getSelectionModel().select(data.node);
18634         this.proxy.update("");
18635         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18636         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18637     },
18638     
18639     getRepairXY : function(e, data){
18640         return data.node.ui.getDDRepairXY();
18641     },
18642     
18643     onEndDrag : function(data, e){
18644         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18645         
18646         
18647     },
18648     
18649     onValidDrop : function(dd, e, id){
18650         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18651         this.hideProxy();
18652     },
18653     
18654     beforeInvalidDrop : function(e, id){
18655         // this scrolls the original position back into view
18656         var sm = this.tree.getSelectionModel();
18657         sm.clearSelections();
18658         sm.select(this.dragData.node);
18659     }
18660 });
18661 }/*
18662  * Based on:
18663  * Ext JS Library 1.1.1
18664  * Copyright(c) 2006-2007, Ext JS, LLC.
18665  *
18666  * Originally Released Under LGPL - original licence link has changed is not relivant.
18667  *
18668  * Fork - LGPL
18669  * <script type="text/javascript">
18670  */
18671 /**
18672  * @class Roo.tree.TreeEditor
18673  * @extends Roo.Editor
18674  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18675  * as the editor field.
18676  * @constructor
18677  * @param {Object} config (used to be the tree panel.)
18678  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18679  * 
18680  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18681  * @cfg {Roo.form.TextField|Object} field The field configuration
18682  *
18683  * 
18684  */
18685 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18686     var tree = config;
18687     var field;
18688     if (oldconfig) { // old style..
18689         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18690     } else {
18691         // new style..
18692         tree = config.tree;
18693         config.field = config.field  || {};
18694         config.field.xtype = 'TextField';
18695         field = Roo.factory(config.field, Roo.form);
18696     }
18697     config = config || {};
18698     
18699     
18700     this.addEvents({
18701         /**
18702          * @event beforenodeedit
18703          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18704          * false from the handler of this event.
18705          * @param {Editor} this
18706          * @param {Roo.tree.Node} node 
18707          */
18708         "beforenodeedit" : true
18709     });
18710     
18711     //Roo.log(config);
18712     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18713
18714     this.tree = tree;
18715
18716     tree.on('beforeclick', this.beforeNodeClick, this);
18717     tree.getTreeEl().on('mousedown', this.hide, this);
18718     this.on('complete', this.updateNode, this);
18719     this.on('beforestartedit', this.fitToTree, this);
18720     this.on('startedit', this.bindScroll, this, {delay:10});
18721     this.on('specialkey', this.onSpecialKey, this);
18722 };
18723
18724 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18725     /**
18726      * @cfg {String} alignment
18727      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18728      */
18729     alignment: "l-l",
18730     // inherit
18731     autoSize: false,
18732     /**
18733      * @cfg {Boolean} hideEl
18734      * True to hide the bound element while the editor is displayed (defaults to false)
18735      */
18736     hideEl : false,
18737     /**
18738      * @cfg {String} cls
18739      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18740      */
18741     cls: "x-small-editor x-tree-editor",
18742     /**
18743      * @cfg {Boolean} shim
18744      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18745      */
18746     shim:false,
18747     // inherit
18748     shadow:"frame",
18749     /**
18750      * @cfg {Number} maxWidth
18751      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18752      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18753      * scroll and client offsets into account prior to each edit.
18754      */
18755     maxWidth: 250,
18756
18757     editDelay : 350,
18758
18759     // private
18760     fitToTree : function(ed, el){
18761         var td = this.tree.getTreeEl().dom, nd = el.dom;
18762         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18763             td.scrollLeft = nd.offsetLeft;
18764         }
18765         var w = Math.min(
18766                 this.maxWidth,
18767                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18768         this.setSize(w, '');
18769         
18770         return this.fireEvent('beforenodeedit', this, this.editNode);
18771         
18772     },
18773
18774     // private
18775     triggerEdit : function(node){
18776         this.completeEdit();
18777         this.editNode = node;
18778         this.startEdit(node.ui.textNode, node.text);
18779     },
18780
18781     // private
18782     bindScroll : function(){
18783         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18784     },
18785
18786     // private
18787     beforeNodeClick : function(node, e){
18788         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18789         this.lastClick = new Date();
18790         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18791             e.stopEvent();
18792             this.triggerEdit(node);
18793             return false;
18794         }
18795         return true;
18796     },
18797
18798     // private
18799     updateNode : function(ed, value){
18800         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18801         this.editNode.setText(value);
18802     },
18803
18804     // private
18805     onHide : function(){
18806         Roo.tree.TreeEditor.superclass.onHide.call(this);
18807         if(this.editNode){
18808             this.editNode.ui.focus();
18809         }
18810     },
18811
18812     // private
18813     onSpecialKey : function(field, e){
18814         var k = e.getKey();
18815         if(k == e.ESC){
18816             e.stopEvent();
18817             this.cancelEdit();
18818         }else if(k == e.ENTER && !e.hasModifier()){
18819             e.stopEvent();
18820             this.completeEdit();
18821         }
18822     }
18823 });//<Script type="text/javascript">
18824 /*
18825  * Based on:
18826  * Ext JS Library 1.1.1
18827  * Copyright(c) 2006-2007, Ext JS, LLC.
18828  *
18829  * Originally Released Under LGPL - original licence link has changed is not relivant.
18830  *
18831  * Fork - LGPL
18832  * <script type="text/javascript">
18833  */
18834  
18835 /**
18836  * Not documented??? - probably should be...
18837  */
18838
18839 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18840     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18841     
18842     renderElements : function(n, a, targetNode, bulkRender){
18843         //consel.log("renderElements?");
18844         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18845
18846         var t = n.getOwnerTree();
18847         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18848         
18849         var cols = t.columns;
18850         var bw = t.borderWidth;
18851         var c = cols[0];
18852         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18853          var cb = typeof a.checked == "boolean";
18854         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18855         var colcls = 'x-t-' + tid + '-c0';
18856         var buf = [
18857             '<li class="x-tree-node">',
18858             
18859                 
18860                 '<div class="x-tree-node-el ', a.cls,'">',
18861                     // extran...
18862                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18863                 
18864                 
18865                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18866                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18867                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18868                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18869                            (a.iconCls ? ' '+a.iconCls : ''),
18870                            '" unselectable="on" />',
18871                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18872                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18873                              
18874                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18875                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18876                             '<span unselectable="on" qtip="' + tx + '">',
18877                              tx,
18878                              '</span></a>' ,
18879                     '</div>',
18880                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18881                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18882                  ];
18883         for(var i = 1, len = cols.length; i < len; i++){
18884             c = cols[i];
18885             colcls = 'x-t-' + tid + '-c' +i;
18886             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18887             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18888                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18889                       "</div>");
18890          }
18891          
18892          buf.push(
18893             '</a>',
18894             '<div class="x-clear"></div></div>',
18895             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18896             "</li>");
18897         
18898         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18899             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18900                                 n.nextSibling.ui.getEl(), buf.join(""));
18901         }else{
18902             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18903         }
18904         var el = this.wrap.firstChild;
18905         this.elRow = el;
18906         this.elNode = el.firstChild;
18907         this.ranchor = el.childNodes[1];
18908         this.ctNode = this.wrap.childNodes[1];
18909         var cs = el.firstChild.childNodes;
18910         this.indentNode = cs[0];
18911         this.ecNode = cs[1];
18912         this.iconNode = cs[2];
18913         var index = 3;
18914         if(cb){
18915             this.checkbox = cs[3];
18916             index++;
18917         }
18918         this.anchor = cs[index];
18919         
18920         this.textNode = cs[index].firstChild;
18921         
18922         //el.on("click", this.onClick, this);
18923         //el.on("dblclick", this.onDblClick, this);
18924         
18925         
18926        // console.log(this);
18927     },
18928     initEvents : function(){
18929         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18930         
18931             
18932         var a = this.ranchor;
18933
18934         var el = Roo.get(a);
18935
18936         if(Roo.isOpera){ // opera render bug ignores the CSS
18937             el.setStyle("text-decoration", "none");
18938         }
18939
18940         el.on("click", this.onClick, this);
18941         el.on("dblclick", this.onDblClick, this);
18942         el.on("contextmenu", this.onContextMenu, this);
18943         
18944     },
18945     
18946     /*onSelectedChange : function(state){
18947         if(state){
18948             this.focus();
18949             this.addClass("x-tree-selected");
18950         }else{
18951             //this.blur();
18952             this.removeClass("x-tree-selected");
18953         }
18954     },*/
18955     addClass : function(cls){
18956         if(this.elRow){
18957             Roo.fly(this.elRow).addClass(cls);
18958         }
18959         
18960     },
18961     
18962     
18963     removeClass : function(cls){
18964         if(this.elRow){
18965             Roo.fly(this.elRow).removeClass(cls);
18966         }
18967     }
18968
18969     
18970     
18971 });//<Script type="text/javascript">
18972
18973 /*
18974  * Based on:
18975  * Ext JS Library 1.1.1
18976  * Copyright(c) 2006-2007, Ext JS, LLC.
18977  *
18978  * Originally Released Under LGPL - original licence link has changed is not relivant.
18979  *
18980  * Fork - LGPL
18981  * <script type="text/javascript">
18982  */
18983  
18984
18985 /**
18986  * @class Roo.tree.ColumnTree
18987  * @extends Roo.data.TreePanel
18988  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18989  * @cfg {int} borderWidth  compined right/left border allowance
18990  * @constructor
18991  * @param {String/HTMLElement/Element} el The container element
18992  * @param {Object} config
18993  */
18994 Roo.tree.ColumnTree =  function(el, config)
18995 {
18996    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18997    this.addEvents({
18998         /**
18999         * @event resize
19000         * Fire this event on a container when it resizes
19001         * @param {int} w Width
19002         * @param {int} h Height
19003         */
19004        "resize" : true
19005     });
19006     this.on('resize', this.onResize, this);
19007 };
19008
19009 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19010     //lines:false,
19011     
19012     
19013     borderWidth: Roo.isBorderBox ? 0 : 2, 
19014     headEls : false,
19015     
19016     render : function(){
19017         // add the header.....
19018        
19019         Roo.tree.ColumnTree.superclass.render.apply(this);
19020         
19021         this.el.addClass('x-column-tree');
19022         
19023         this.headers = this.el.createChild(
19024             {cls:'x-tree-headers'},this.innerCt.dom);
19025    
19026         var cols = this.columns, c;
19027         var totalWidth = 0;
19028         this.headEls = [];
19029         var  len = cols.length;
19030         for(var i = 0; i < len; i++){
19031              c = cols[i];
19032              totalWidth += c.width;
19033             this.headEls.push(this.headers.createChild({
19034                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19035                  cn: {
19036                      cls:'x-tree-hd-text',
19037                      html: c.header
19038                  },
19039                  style:'width:'+(c.width-this.borderWidth)+'px;'
19040              }));
19041         }
19042         this.headers.createChild({cls:'x-clear'});
19043         // prevent floats from wrapping when clipped
19044         this.headers.setWidth(totalWidth);
19045         //this.innerCt.setWidth(totalWidth);
19046         this.innerCt.setStyle({ overflow: 'auto' });
19047         this.onResize(this.width, this.height);
19048              
19049         
19050     },
19051     onResize : function(w,h)
19052     {
19053         this.height = h;
19054         this.width = w;
19055         // resize cols..
19056         this.innerCt.setWidth(this.width);
19057         this.innerCt.setHeight(this.height-20);
19058         
19059         // headers...
19060         var cols = this.columns, c;
19061         var totalWidth = 0;
19062         var expEl = false;
19063         var len = cols.length;
19064         for(var i = 0; i < len; i++){
19065             c = cols[i];
19066             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19067                 // it's the expander..
19068                 expEl  = this.headEls[i];
19069                 continue;
19070             }
19071             totalWidth += c.width;
19072             
19073         }
19074         if (expEl) {
19075             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19076         }
19077         this.headers.setWidth(w-20);
19078
19079         
19080         
19081         
19082     }
19083 });
19084 /*
19085  * Based on:
19086  * Ext JS Library 1.1.1
19087  * Copyright(c) 2006-2007, Ext JS, LLC.
19088  *
19089  * Originally Released Under LGPL - original licence link has changed is not relivant.
19090  *
19091  * Fork - LGPL
19092  * <script type="text/javascript">
19093  */
19094  
19095 /**
19096  * @class Roo.menu.Menu
19097  * @extends Roo.util.Observable
19098  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19099  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19100  * @constructor
19101  * Creates a new Menu
19102  * @param {Object} config Configuration options
19103  */
19104 Roo.menu.Menu = function(config){
19105     Roo.apply(this, config);
19106     this.id = this.id || Roo.id();
19107     this.addEvents({
19108         /**
19109          * @event beforeshow
19110          * Fires before this menu is displayed
19111          * @param {Roo.menu.Menu} this
19112          */
19113         beforeshow : true,
19114         /**
19115          * @event beforehide
19116          * Fires before this menu is hidden
19117          * @param {Roo.menu.Menu} this
19118          */
19119         beforehide : true,
19120         /**
19121          * @event show
19122          * Fires after this menu is displayed
19123          * @param {Roo.menu.Menu} this
19124          */
19125         show : true,
19126         /**
19127          * @event hide
19128          * Fires after this menu is hidden
19129          * @param {Roo.menu.Menu} this
19130          */
19131         hide : true,
19132         /**
19133          * @event click
19134          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19135          * @param {Roo.menu.Menu} this
19136          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19137          * @param {Roo.EventObject} e
19138          */
19139         click : true,
19140         /**
19141          * @event mouseover
19142          * Fires when the mouse is hovering over this menu
19143          * @param {Roo.menu.Menu} this
19144          * @param {Roo.EventObject} e
19145          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19146          */
19147         mouseover : true,
19148         /**
19149          * @event mouseout
19150          * Fires when the mouse exits this menu
19151          * @param {Roo.menu.Menu} this
19152          * @param {Roo.EventObject} e
19153          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19154          */
19155         mouseout : true,
19156         /**
19157          * @event itemclick
19158          * Fires when a menu item contained in this menu is clicked
19159          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19160          * @param {Roo.EventObject} e
19161          */
19162         itemclick: true
19163     });
19164     if (this.registerMenu) {
19165         Roo.menu.MenuMgr.register(this);
19166     }
19167     
19168     var mis = this.items;
19169     this.items = new Roo.util.MixedCollection();
19170     if(mis){
19171         this.add.apply(this, mis);
19172     }
19173 };
19174
19175 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19176     /**
19177      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19178      */
19179     minWidth : 120,
19180     /**
19181      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19182      * for bottom-right shadow (defaults to "sides")
19183      */
19184     shadow : "sides",
19185     /**
19186      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19187      * this menu (defaults to "tl-tr?")
19188      */
19189     subMenuAlign : "tl-tr?",
19190     /**
19191      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19192      * relative to its element of origin (defaults to "tl-bl?")
19193      */
19194     defaultAlign : "tl-bl?",
19195     /**
19196      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19197      */
19198     allowOtherMenus : false,
19199     /**
19200      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19201      */
19202     registerMenu : true,
19203
19204     hidden:true,
19205
19206     // private
19207     render : function(){
19208         if(this.el){
19209             return;
19210         }
19211         var el = this.el = new Roo.Layer({
19212             cls: "x-menu",
19213             shadow:this.shadow,
19214             constrain: false,
19215             parentEl: this.parentEl || document.body,
19216             zindex:15000
19217         });
19218
19219         this.keyNav = new Roo.menu.MenuNav(this);
19220
19221         if(this.plain){
19222             el.addClass("x-menu-plain");
19223         }
19224         if(this.cls){
19225             el.addClass(this.cls);
19226         }
19227         // generic focus element
19228         this.focusEl = el.createChild({
19229             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19230         });
19231         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19232         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19233         
19234         ul.on("mouseover", this.onMouseOver, this);
19235         ul.on("mouseout", this.onMouseOut, this);
19236         this.items.each(function(item){
19237             if (item.hidden) {
19238                 return;
19239             }
19240             
19241             var li = document.createElement("li");
19242             li.className = "x-menu-list-item";
19243             ul.dom.appendChild(li);
19244             item.render(li, this);
19245         }, this);
19246         this.ul = ul;
19247         this.autoWidth();
19248     },
19249
19250     // private
19251     autoWidth : function(){
19252         var el = this.el, ul = this.ul;
19253         if(!el){
19254             return;
19255         }
19256         var w = this.width;
19257         if(w){
19258             el.setWidth(w);
19259         }else if(Roo.isIE){
19260             el.setWidth(this.minWidth);
19261             var t = el.dom.offsetWidth; // force recalc
19262             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19263         }
19264     },
19265
19266     // private
19267     delayAutoWidth : function(){
19268         if(this.rendered){
19269             if(!this.awTask){
19270                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19271             }
19272             this.awTask.delay(20);
19273         }
19274     },
19275
19276     // private
19277     findTargetItem : function(e){
19278         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19279         if(t && t.menuItemId){
19280             return this.items.get(t.menuItemId);
19281         }
19282     },
19283
19284     // private
19285     onClick : function(e){
19286         Roo.log("menu.onClick");
19287         var t = this.findTargetItem(e);
19288         if(!t){
19289             return;
19290         }
19291         Roo.log(e);
19292         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19293             if(t == this.activeItem && t.shouldDeactivate(e)){
19294                 this.activeItem.deactivate();
19295                 delete this.activeItem;
19296                 return;
19297             }
19298             if(t.canActivate){
19299                 this.setActiveItem(t, true);
19300             }
19301             return;
19302             
19303             
19304         }
19305         
19306         t.onClick(e);
19307         this.fireEvent("click", this, t, e);
19308     },
19309
19310     // private
19311     setActiveItem : function(item, autoExpand){
19312         if(item != this.activeItem){
19313             if(this.activeItem){
19314                 this.activeItem.deactivate();
19315             }
19316             this.activeItem = item;
19317             item.activate(autoExpand);
19318         }else if(autoExpand){
19319             item.expandMenu();
19320         }
19321     },
19322
19323     // private
19324     tryActivate : function(start, step){
19325         var items = this.items;
19326         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19327             var item = items.get(i);
19328             if(!item.disabled && item.canActivate){
19329                 this.setActiveItem(item, false);
19330                 return item;
19331             }
19332         }
19333         return false;
19334     },
19335
19336     // private
19337     onMouseOver : function(e){
19338         var t;
19339         if(t = this.findTargetItem(e)){
19340             if(t.canActivate && !t.disabled){
19341                 this.setActiveItem(t, true);
19342             }
19343         }
19344         this.fireEvent("mouseover", this, e, t);
19345     },
19346
19347     // private
19348     onMouseOut : function(e){
19349         var t;
19350         if(t = this.findTargetItem(e)){
19351             if(t == this.activeItem && t.shouldDeactivate(e)){
19352                 this.activeItem.deactivate();
19353                 delete this.activeItem;
19354             }
19355         }
19356         this.fireEvent("mouseout", this, e, t);
19357     },
19358
19359     /**
19360      * Read-only.  Returns true if the menu is currently displayed, else false.
19361      * @type Boolean
19362      */
19363     isVisible : function(){
19364         return this.el && !this.hidden;
19365     },
19366
19367     /**
19368      * Displays this menu relative to another element
19369      * @param {String/HTMLElement/Roo.Element} element The element to align to
19370      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19371      * the element (defaults to this.defaultAlign)
19372      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19373      */
19374     show : function(el, pos, parentMenu){
19375         this.parentMenu = parentMenu;
19376         if(!this.el){
19377             this.render();
19378         }
19379         this.fireEvent("beforeshow", this);
19380         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19381     },
19382
19383     /**
19384      * Displays this menu at a specific xy position
19385      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19386      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19387      */
19388     showAt : function(xy, parentMenu, /* private: */_e){
19389         this.parentMenu = parentMenu;
19390         if(!this.el){
19391             this.render();
19392         }
19393         if(_e !== false){
19394             this.fireEvent("beforeshow", this);
19395             xy = this.el.adjustForConstraints(xy);
19396         }
19397         this.el.setXY(xy);
19398         this.el.show();
19399         this.hidden = false;
19400         this.focus();
19401         this.fireEvent("show", this);
19402     },
19403
19404     focus : function(){
19405         if(!this.hidden){
19406             this.doFocus.defer(50, this);
19407         }
19408     },
19409
19410     doFocus : function(){
19411         if(!this.hidden){
19412             this.focusEl.focus();
19413         }
19414     },
19415
19416     /**
19417      * Hides this menu and optionally all parent menus
19418      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19419      */
19420     hide : function(deep){
19421         if(this.el && this.isVisible()){
19422             this.fireEvent("beforehide", this);
19423             if(this.activeItem){
19424                 this.activeItem.deactivate();
19425                 this.activeItem = null;
19426             }
19427             this.el.hide();
19428             this.hidden = true;
19429             this.fireEvent("hide", this);
19430         }
19431         if(deep === true && this.parentMenu){
19432             this.parentMenu.hide(true);
19433         }
19434     },
19435
19436     /**
19437      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19438      * Any of the following are valid:
19439      * <ul>
19440      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19441      * <li>An HTMLElement object which will be converted to a menu item</li>
19442      * <li>A menu item config object that will be created as a new menu item</li>
19443      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19444      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19445      * </ul>
19446      * Usage:
19447      * <pre><code>
19448 // Create the menu
19449 var menu = new Roo.menu.Menu();
19450
19451 // Create a menu item to add by reference
19452 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19453
19454 // Add a bunch of items at once using different methods.
19455 // Only the last item added will be returned.
19456 var item = menu.add(
19457     menuItem,                // add existing item by ref
19458     'Dynamic Item',          // new TextItem
19459     '-',                     // new separator
19460     { text: 'Config Item' }  // new item by config
19461 );
19462 </code></pre>
19463      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19464      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19465      */
19466     add : function(){
19467         var a = arguments, l = a.length, item;
19468         for(var i = 0; i < l; i++){
19469             var el = a[i];
19470             if ((typeof(el) == "object") && el.xtype && el.xns) {
19471                 el = Roo.factory(el, Roo.menu);
19472             }
19473             
19474             if(el.render){ // some kind of Item
19475                 item = this.addItem(el);
19476             }else if(typeof el == "string"){ // string
19477                 if(el == "separator" || el == "-"){
19478                     item = this.addSeparator();
19479                 }else{
19480                     item = this.addText(el);
19481                 }
19482             }else if(el.tagName || el.el){ // element
19483                 item = this.addElement(el);
19484             }else if(typeof el == "object"){ // must be menu item config?
19485                 item = this.addMenuItem(el);
19486             }
19487         }
19488         return item;
19489     },
19490
19491     /**
19492      * Returns this menu's underlying {@link Roo.Element} object
19493      * @return {Roo.Element} The element
19494      */
19495     getEl : function(){
19496         if(!this.el){
19497             this.render();
19498         }
19499         return this.el;
19500     },
19501
19502     /**
19503      * Adds a separator bar to the menu
19504      * @return {Roo.menu.Item} The menu item that was added
19505      */
19506     addSeparator : function(){
19507         return this.addItem(new Roo.menu.Separator());
19508     },
19509
19510     /**
19511      * Adds an {@link Roo.Element} object to the menu
19512      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19513      * @return {Roo.menu.Item} The menu item that was added
19514      */
19515     addElement : function(el){
19516         return this.addItem(new Roo.menu.BaseItem(el));
19517     },
19518
19519     /**
19520      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19521      * @param {Roo.menu.Item} item The menu item to add
19522      * @return {Roo.menu.Item} The menu item that was added
19523      */
19524     addItem : function(item){
19525         this.items.add(item);
19526         if(this.ul){
19527             var li = document.createElement("li");
19528             li.className = "x-menu-list-item";
19529             this.ul.dom.appendChild(li);
19530             item.render(li, this);
19531             this.delayAutoWidth();
19532         }
19533         return item;
19534     },
19535
19536     /**
19537      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19538      * @param {Object} config A MenuItem config object
19539      * @return {Roo.menu.Item} The menu item that was added
19540      */
19541     addMenuItem : function(config){
19542         if(!(config instanceof Roo.menu.Item)){
19543             if(typeof config.checked == "boolean"){ // must be check menu item config?
19544                 config = new Roo.menu.CheckItem(config);
19545             }else{
19546                 config = new Roo.menu.Item(config);
19547             }
19548         }
19549         return this.addItem(config);
19550     },
19551
19552     /**
19553      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19554      * @param {String} text The text to display in the menu item
19555      * @return {Roo.menu.Item} The menu item that was added
19556      */
19557     addText : function(text){
19558         return this.addItem(new Roo.menu.TextItem({ text : text }));
19559     },
19560
19561     /**
19562      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19563      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19564      * @param {Roo.menu.Item} item The menu item to add
19565      * @return {Roo.menu.Item} The menu item that was added
19566      */
19567     insert : function(index, item){
19568         this.items.insert(index, item);
19569         if(this.ul){
19570             var li = document.createElement("li");
19571             li.className = "x-menu-list-item";
19572             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19573             item.render(li, this);
19574             this.delayAutoWidth();
19575         }
19576         return item;
19577     },
19578
19579     /**
19580      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19581      * @param {Roo.menu.Item} item The menu item to remove
19582      */
19583     remove : function(item){
19584         this.items.removeKey(item.id);
19585         item.destroy();
19586     },
19587
19588     /**
19589      * Removes and destroys all items in the menu
19590      */
19591     removeAll : function(){
19592         var f;
19593         while(f = this.items.first()){
19594             this.remove(f);
19595         }
19596     }
19597 });
19598
19599 // MenuNav is a private utility class used internally by the Menu
19600 Roo.menu.MenuNav = function(menu){
19601     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19602     this.scope = this.menu = menu;
19603 };
19604
19605 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19606     doRelay : function(e, h){
19607         var k = e.getKey();
19608         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19609             this.menu.tryActivate(0, 1);
19610             return false;
19611         }
19612         return h.call(this.scope || this, e, this.menu);
19613     },
19614
19615     up : function(e, m){
19616         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19617             m.tryActivate(m.items.length-1, -1);
19618         }
19619     },
19620
19621     down : function(e, m){
19622         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19623             m.tryActivate(0, 1);
19624         }
19625     },
19626
19627     right : function(e, m){
19628         if(m.activeItem){
19629             m.activeItem.expandMenu(true);
19630         }
19631     },
19632
19633     left : function(e, m){
19634         m.hide();
19635         if(m.parentMenu && m.parentMenu.activeItem){
19636             m.parentMenu.activeItem.activate();
19637         }
19638     },
19639
19640     enter : function(e, m){
19641         if(m.activeItem){
19642             e.stopPropagation();
19643             m.activeItem.onClick(e);
19644             m.fireEvent("click", this, m.activeItem);
19645             return true;
19646         }
19647     }
19648 });/*
19649  * Based on:
19650  * Ext JS Library 1.1.1
19651  * Copyright(c) 2006-2007, Ext JS, LLC.
19652  *
19653  * Originally Released Under LGPL - original licence link has changed is not relivant.
19654  *
19655  * Fork - LGPL
19656  * <script type="text/javascript">
19657  */
19658  
19659 /**
19660  * @class Roo.menu.MenuMgr
19661  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19662  * @singleton
19663  */
19664 Roo.menu.MenuMgr = function(){
19665    var menus, active, groups = {}, attached = false, lastShow = new Date();
19666
19667    // private - called when first menu is created
19668    function init(){
19669        menus = {};
19670        active = new Roo.util.MixedCollection();
19671        Roo.get(document).addKeyListener(27, function(){
19672            if(active.length > 0){
19673                hideAll();
19674            }
19675        });
19676    }
19677
19678    // private
19679    function hideAll(){
19680        if(active && active.length > 0){
19681            var c = active.clone();
19682            c.each(function(m){
19683                m.hide();
19684            });
19685        }
19686    }
19687
19688    // private
19689    function onHide(m){
19690        active.remove(m);
19691        if(active.length < 1){
19692            Roo.get(document).un("mousedown", onMouseDown);
19693            attached = false;
19694        }
19695    }
19696
19697    // private
19698    function onShow(m){
19699        var last = active.last();
19700        lastShow = new Date();
19701        active.add(m);
19702        if(!attached){
19703            Roo.get(document).on("mousedown", onMouseDown);
19704            attached = true;
19705        }
19706        if(m.parentMenu){
19707           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19708           m.parentMenu.activeChild = m;
19709        }else if(last && last.isVisible()){
19710           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19711        }
19712    }
19713
19714    // private
19715    function onBeforeHide(m){
19716        if(m.activeChild){
19717            m.activeChild.hide();
19718        }
19719        if(m.autoHideTimer){
19720            clearTimeout(m.autoHideTimer);
19721            delete m.autoHideTimer;
19722        }
19723    }
19724
19725    // private
19726    function onBeforeShow(m){
19727        var pm = m.parentMenu;
19728        if(!pm && !m.allowOtherMenus){
19729            hideAll();
19730        }else if(pm && pm.activeChild && active != m){
19731            pm.activeChild.hide();
19732        }
19733    }
19734
19735    // private
19736    function onMouseDown(e){
19737        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19738            hideAll();
19739        }
19740    }
19741
19742    // private
19743    function onBeforeCheck(mi, state){
19744        if(state){
19745            var g = groups[mi.group];
19746            for(var i = 0, l = g.length; i < l; i++){
19747                if(g[i] != mi){
19748                    g[i].setChecked(false);
19749                }
19750            }
19751        }
19752    }
19753
19754    return {
19755
19756        /**
19757         * Hides all menus that are currently visible
19758         */
19759        hideAll : function(){
19760             hideAll();  
19761        },
19762
19763        // private
19764        register : function(menu){
19765            if(!menus){
19766                init();
19767            }
19768            menus[menu.id] = menu;
19769            menu.on("beforehide", onBeforeHide);
19770            menu.on("hide", onHide);
19771            menu.on("beforeshow", onBeforeShow);
19772            menu.on("show", onShow);
19773            var g = menu.group;
19774            if(g && menu.events["checkchange"]){
19775                if(!groups[g]){
19776                    groups[g] = [];
19777                }
19778                groups[g].push(menu);
19779                menu.on("checkchange", onCheck);
19780            }
19781        },
19782
19783         /**
19784          * Returns a {@link Roo.menu.Menu} object
19785          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19786          * be used to generate and return a new Menu instance.
19787          */
19788        get : function(menu){
19789            if(typeof menu == "string"){ // menu id
19790                return menus[menu];
19791            }else if(menu.events){  // menu instance
19792                return menu;
19793            }else if(typeof menu.length == 'number'){ // array of menu items?
19794                return new Roo.menu.Menu({items:menu});
19795            }else{ // otherwise, must be a config
19796                return new Roo.menu.Menu(menu);
19797            }
19798        },
19799
19800        // private
19801        unregister : function(menu){
19802            delete menus[menu.id];
19803            menu.un("beforehide", onBeforeHide);
19804            menu.un("hide", onHide);
19805            menu.un("beforeshow", onBeforeShow);
19806            menu.un("show", onShow);
19807            var g = menu.group;
19808            if(g && menu.events["checkchange"]){
19809                groups[g].remove(menu);
19810                menu.un("checkchange", onCheck);
19811            }
19812        },
19813
19814        // private
19815        registerCheckable : function(menuItem){
19816            var g = menuItem.group;
19817            if(g){
19818                if(!groups[g]){
19819                    groups[g] = [];
19820                }
19821                groups[g].push(menuItem);
19822                menuItem.on("beforecheckchange", onBeforeCheck);
19823            }
19824        },
19825
19826        // private
19827        unregisterCheckable : function(menuItem){
19828            var g = menuItem.group;
19829            if(g){
19830                groups[g].remove(menuItem);
19831                menuItem.un("beforecheckchange", onBeforeCheck);
19832            }
19833        }
19834    };
19835 }();/*
19836  * Based on:
19837  * Ext JS Library 1.1.1
19838  * Copyright(c) 2006-2007, Ext JS, LLC.
19839  *
19840  * Originally Released Under LGPL - original licence link has changed is not relivant.
19841  *
19842  * Fork - LGPL
19843  * <script type="text/javascript">
19844  */
19845  
19846
19847 /**
19848  * @class Roo.menu.BaseItem
19849  * @extends Roo.Component
19850  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19851  * management and base configuration options shared by all menu components.
19852  * @constructor
19853  * Creates a new BaseItem
19854  * @param {Object} config Configuration options
19855  */
19856 Roo.menu.BaseItem = function(config){
19857     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19858
19859     this.addEvents({
19860         /**
19861          * @event click
19862          * Fires when this item is clicked
19863          * @param {Roo.menu.BaseItem} this
19864          * @param {Roo.EventObject} e
19865          */
19866         click: true,
19867         /**
19868          * @event activate
19869          * Fires when this item is activated
19870          * @param {Roo.menu.BaseItem} this
19871          */
19872         activate : true,
19873         /**
19874          * @event deactivate
19875          * Fires when this item is deactivated
19876          * @param {Roo.menu.BaseItem} this
19877          */
19878         deactivate : true
19879     });
19880
19881     if(this.handler){
19882         this.on("click", this.handler, this.scope, true);
19883     }
19884 };
19885
19886 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19887     /**
19888      * @cfg {Function} handler
19889      * A function that will handle the click event of this menu item (defaults to undefined)
19890      */
19891     /**
19892      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19893      */
19894     canActivate : false,
19895     
19896      /**
19897      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19898      */
19899     hidden: false,
19900     
19901     /**
19902      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19903      */
19904     activeClass : "x-menu-item-active",
19905     /**
19906      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19907      */
19908     hideOnClick : true,
19909     /**
19910      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19911      */
19912     hideDelay : 100,
19913
19914     // private
19915     ctype: "Roo.menu.BaseItem",
19916
19917     // private
19918     actionMode : "container",
19919
19920     // private
19921     render : function(container, parentMenu){
19922         this.parentMenu = parentMenu;
19923         Roo.menu.BaseItem.superclass.render.call(this, container);
19924         this.container.menuItemId = this.id;
19925     },
19926
19927     // private
19928     onRender : function(container, position){
19929         this.el = Roo.get(this.el);
19930         container.dom.appendChild(this.el.dom);
19931     },
19932
19933     // private
19934     onClick : function(e){
19935         if(!this.disabled && this.fireEvent("click", this, e) !== false
19936                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19937             this.handleClick(e);
19938         }else{
19939             e.stopEvent();
19940         }
19941     },
19942
19943     // private
19944     activate : function(){
19945         if(this.disabled){
19946             return false;
19947         }
19948         var li = this.container;
19949         li.addClass(this.activeClass);
19950         this.region = li.getRegion().adjust(2, 2, -2, -2);
19951         this.fireEvent("activate", this);
19952         return true;
19953     },
19954
19955     // private
19956     deactivate : function(){
19957         this.container.removeClass(this.activeClass);
19958         this.fireEvent("deactivate", this);
19959     },
19960
19961     // private
19962     shouldDeactivate : function(e){
19963         return !this.region || !this.region.contains(e.getPoint());
19964     },
19965
19966     // private
19967     handleClick : function(e){
19968         if(this.hideOnClick){
19969             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19970         }
19971     },
19972
19973     // private
19974     expandMenu : function(autoActivate){
19975         // do nothing
19976     },
19977
19978     // private
19979     hideMenu : function(){
19980         // do nothing
19981     }
19982 });/*
19983  * Based on:
19984  * Ext JS Library 1.1.1
19985  * Copyright(c) 2006-2007, Ext JS, LLC.
19986  *
19987  * Originally Released Under LGPL - original licence link has changed is not relivant.
19988  *
19989  * Fork - LGPL
19990  * <script type="text/javascript">
19991  */
19992  
19993 /**
19994  * @class Roo.menu.Adapter
19995  * @extends Roo.menu.BaseItem
19996  * 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.
19997  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19998  * @constructor
19999  * Creates a new Adapter
20000  * @param {Object} config Configuration options
20001  */
20002 Roo.menu.Adapter = function(component, config){
20003     Roo.menu.Adapter.superclass.constructor.call(this, config);
20004     this.component = component;
20005 };
20006 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20007     // private
20008     canActivate : true,
20009
20010     // private
20011     onRender : function(container, position){
20012         this.component.render(container);
20013         this.el = this.component.getEl();
20014     },
20015
20016     // private
20017     activate : function(){
20018         if(this.disabled){
20019             return false;
20020         }
20021         this.component.focus();
20022         this.fireEvent("activate", this);
20023         return true;
20024     },
20025
20026     // private
20027     deactivate : function(){
20028         this.fireEvent("deactivate", this);
20029     },
20030
20031     // private
20032     disable : function(){
20033         this.component.disable();
20034         Roo.menu.Adapter.superclass.disable.call(this);
20035     },
20036
20037     // private
20038     enable : function(){
20039         this.component.enable();
20040         Roo.menu.Adapter.superclass.enable.call(this);
20041     }
20042 });/*
20043  * Based on:
20044  * Ext JS Library 1.1.1
20045  * Copyright(c) 2006-2007, Ext JS, LLC.
20046  *
20047  * Originally Released Under LGPL - original licence link has changed is not relivant.
20048  *
20049  * Fork - LGPL
20050  * <script type="text/javascript">
20051  */
20052
20053 /**
20054  * @class Roo.menu.TextItem
20055  * @extends Roo.menu.BaseItem
20056  * Adds a static text string to a menu, usually used as either a heading or group separator.
20057  * Note: old style constructor with text is still supported.
20058  * 
20059  * @constructor
20060  * Creates a new TextItem
20061  * @param {Object} cfg Configuration
20062  */
20063 Roo.menu.TextItem = function(cfg){
20064     if (typeof(cfg) == 'string') {
20065         this.text = cfg;
20066     } else {
20067         Roo.apply(this,cfg);
20068     }
20069     
20070     Roo.menu.TextItem.superclass.constructor.call(this);
20071 };
20072
20073 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20074     /**
20075      * @cfg {Boolean} text Text to show on item.
20076      */
20077     text : '',
20078     
20079     /**
20080      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20081      */
20082     hideOnClick : false,
20083     /**
20084      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20085      */
20086     itemCls : "x-menu-text",
20087
20088     // private
20089     onRender : function(){
20090         var s = document.createElement("span");
20091         s.className = this.itemCls;
20092         s.innerHTML = this.text;
20093         this.el = s;
20094         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20095     }
20096 });/*
20097  * Based on:
20098  * Ext JS Library 1.1.1
20099  * Copyright(c) 2006-2007, Ext JS, LLC.
20100  *
20101  * Originally Released Under LGPL - original licence link has changed is not relivant.
20102  *
20103  * Fork - LGPL
20104  * <script type="text/javascript">
20105  */
20106
20107 /**
20108  * @class Roo.menu.Separator
20109  * @extends Roo.menu.BaseItem
20110  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20111  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20112  * @constructor
20113  * @param {Object} config Configuration options
20114  */
20115 Roo.menu.Separator = function(config){
20116     Roo.menu.Separator.superclass.constructor.call(this, config);
20117 };
20118
20119 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20120     /**
20121      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20122      */
20123     itemCls : "x-menu-sep",
20124     /**
20125      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20126      */
20127     hideOnClick : false,
20128
20129     // private
20130     onRender : function(li){
20131         var s = document.createElement("span");
20132         s.className = this.itemCls;
20133         s.innerHTML = "&#160;";
20134         this.el = s;
20135         li.addClass("x-menu-sep-li");
20136         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20137     }
20138 });/*
20139  * Based on:
20140  * Ext JS Library 1.1.1
20141  * Copyright(c) 2006-2007, Ext JS, LLC.
20142  *
20143  * Originally Released Under LGPL - original licence link has changed is not relivant.
20144  *
20145  * Fork - LGPL
20146  * <script type="text/javascript">
20147  */
20148 /**
20149  * @class Roo.menu.Item
20150  * @extends Roo.menu.BaseItem
20151  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20152  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20153  * activation and click handling.
20154  * @constructor
20155  * Creates a new Item
20156  * @param {Object} config Configuration options
20157  */
20158 Roo.menu.Item = function(config){
20159     Roo.menu.Item.superclass.constructor.call(this, config);
20160     if(this.menu){
20161         this.menu = Roo.menu.MenuMgr.get(this.menu);
20162     }
20163 };
20164 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20165     
20166     /**
20167      * @cfg {String} text
20168      * The text to show on the menu item.
20169      */
20170     text: '',
20171      /**
20172      * @cfg {String} HTML to render in menu
20173      * The text to show on the menu item (HTML version).
20174      */
20175     html: '',
20176     /**
20177      * @cfg {String} icon
20178      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20179      */
20180     icon: undefined,
20181     /**
20182      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20183      */
20184     itemCls : "x-menu-item",
20185     /**
20186      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20187      */
20188     canActivate : true,
20189     /**
20190      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20191      */
20192     showDelay: 200,
20193     // doc'd in BaseItem
20194     hideDelay: 200,
20195
20196     // private
20197     ctype: "Roo.menu.Item",
20198     
20199     // private
20200     onRender : function(container, position){
20201         var el = document.createElement("a");
20202         el.hideFocus = true;
20203         el.unselectable = "on";
20204         el.href = this.href || "#";
20205         if(this.hrefTarget){
20206             el.target = this.hrefTarget;
20207         }
20208         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20209         
20210         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20211         
20212         el.innerHTML = String.format(
20213                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20214                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20215         this.el = el;
20216         Roo.menu.Item.superclass.onRender.call(this, container, position);
20217     },
20218
20219     /**
20220      * Sets the text to display in this menu item
20221      * @param {String} text The text to display
20222      * @param {Boolean} isHTML true to indicate text is pure html.
20223      */
20224     setText : function(text, isHTML){
20225         if (isHTML) {
20226             this.html = text;
20227         } else {
20228             this.text = text;
20229             this.html = '';
20230         }
20231         if(this.rendered){
20232             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20233      
20234             this.el.update(String.format(
20235                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20236                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20237             this.parentMenu.autoWidth();
20238         }
20239     },
20240
20241     // private
20242     handleClick : function(e){
20243         if(!this.href){ // if no link defined, stop the event automatically
20244             e.stopEvent();
20245         }
20246         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20247     },
20248
20249     // private
20250     activate : function(autoExpand){
20251         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20252             this.focus();
20253             if(autoExpand){
20254                 this.expandMenu();
20255             }
20256         }
20257         return true;
20258     },
20259
20260     // private
20261     shouldDeactivate : function(e){
20262         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20263             if(this.menu && this.menu.isVisible()){
20264                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20265             }
20266             return true;
20267         }
20268         return false;
20269     },
20270
20271     // private
20272     deactivate : function(){
20273         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20274         this.hideMenu();
20275     },
20276
20277     // private
20278     expandMenu : function(autoActivate){
20279         if(!this.disabled && this.menu){
20280             clearTimeout(this.hideTimer);
20281             delete this.hideTimer;
20282             if(!this.menu.isVisible() && !this.showTimer){
20283                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20284             }else if (this.menu.isVisible() && autoActivate){
20285                 this.menu.tryActivate(0, 1);
20286             }
20287         }
20288     },
20289
20290     // private
20291     deferExpand : function(autoActivate){
20292         delete this.showTimer;
20293         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20294         if(autoActivate){
20295             this.menu.tryActivate(0, 1);
20296         }
20297     },
20298
20299     // private
20300     hideMenu : function(){
20301         clearTimeout(this.showTimer);
20302         delete this.showTimer;
20303         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20304             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20305         }
20306     },
20307
20308     // private
20309     deferHide : function(){
20310         delete this.hideTimer;
20311         this.menu.hide();
20312     }
20313 });/*
20314  * Based on:
20315  * Ext JS Library 1.1.1
20316  * Copyright(c) 2006-2007, Ext JS, LLC.
20317  *
20318  * Originally Released Under LGPL - original licence link has changed is not relivant.
20319  *
20320  * Fork - LGPL
20321  * <script type="text/javascript">
20322  */
20323  
20324 /**
20325  * @class Roo.menu.CheckItem
20326  * @extends Roo.menu.Item
20327  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20328  * @constructor
20329  * Creates a new CheckItem
20330  * @param {Object} config Configuration options
20331  */
20332 Roo.menu.CheckItem = function(config){
20333     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20334     this.addEvents({
20335         /**
20336          * @event beforecheckchange
20337          * Fires before the checked value is set, providing an opportunity to cancel if needed
20338          * @param {Roo.menu.CheckItem} this
20339          * @param {Boolean} checked The new checked value that will be set
20340          */
20341         "beforecheckchange" : true,
20342         /**
20343          * @event checkchange
20344          * Fires after the checked value has been set
20345          * @param {Roo.menu.CheckItem} this
20346          * @param {Boolean} checked The checked value that was set
20347          */
20348         "checkchange" : true
20349     });
20350     if(this.checkHandler){
20351         this.on('checkchange', this.checkHandler, this.scope);
20352     }
20353 };
20354 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20355     /**
20356      * @cfg {String} group
20357      * All check items with the same group name will automatically be grouped into a single-select
20358      * radio button group (defaults to '')
20359      */
20360     /**
20361      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20362      */
20363     itemCls : "x-menu-item x-menu-check-item",
20364     /**
20365      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20366      */
20367     groupClass : "x-menu-group-item",
20368
20369     /**
20370      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20371      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20372      * initialized with checked = true will be rendered as checked.
20373      */
20374     checked: false,
20375
20376     // private
20377     ctype: "Roo.menu.CheckItem",
20378
20379     // private
20380     onRender : function(c){
20381         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20382         if(this.group){
20383             this.el.addClass(this.groupClass);
20384         }
20385         Roo.menu.MenuMgr.registerCheckable(this);
20386         if(this.checked){
20387             this.checked = false;
20388             this.setChecked(true, true);
20389         }
20390     },
20391
20392     // private
20393     destroy : function(){
20394         if(this.rendered){
20395             Roo.menu.MenuMgr.unregisterCheckable(this);
20396         }
20397         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20398     },
20399
20400     /**
20401      * Set the checked state of this item
20402      * @param {Boolean} checked The new checked value
20403      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20404      */
20405     setChecked : function(state, suppressEvent){
20406         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20407             if(this.container){
20408                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20409             }
20410             this.checked = state;
20411             if(suppressEvent !== true){
20412                 this.fireEvent("checkchange", this, state);
20413             }
20414         }
20415     },
20416
20417     // private
20418     handleClick : function(e){
20419        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20420            this.setChecked(!this.checked);
20421        }
20422        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20423     }
20424 });/*
20425  * Based on:
20426  * Ext JS Library 1.1.1
20427  * Copyright(c) 2006-2007, Ext JS, LLC.
20428  *
20429  * Originally Released Under LGPL - original licence link has changed is not relivant.
20430  *
20431  * Fork - LGPL
20432  * <script type="text/javascript">
20433  */
20434  
20435 /**
20436  * @class Roo.menu.DateItem
20437  * @extends Roo.menu.Adapter
20438  * A menu item that wraps the {@link Roo.DatPicker} component.
20439  * @constructor
20440  * Creates a new DateItem
20441  * @param {Object} config Configuration options
20442  */
20443 Roo.menu.DateItem = function(config){
20444     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20445     /** The Roo.DatePicker object @type Roo.DatePicker */
20446     this.picker = this.component;
20447     this.addEvents({select: true});
20448     
20449     this.picker.on("render", function(picker){
20450         picker.getEl().swallowEvent("click");
20451         picker.container.addClass("x-menu-date-item");
20452     });
20453
20454     this.picker.on("select", this.onSelect, this);
20455 };
20456
20457 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20458     // private
20459     onSelect : function(picker, date){
20460         this.fireEvent("select", this, date, picker);
20461         Roo.menu.DateItem.superclass.handleClick.call(this);
20462     }
20463 });/*
20464  * Based on:
20465  * Ext JS Library 1.1.1
20466  * Copyright(c) 2006-2007, Ext JS, LLC.
20467  *
20468  * Originally Released Under LGPL - original licence link has changed is not relivant.
20469  *
20470  * Fork - LGPL
20471  * <script type="text/javascript">
20472  */
20473  
20474 /**
20475  * @class Roo.menu.ColorItem
20476  * @extends Roo.menu.Adapter
20477  * A menu item that wraps the {@link Roo.ColorPalette} component.
20478  * @constructor
20479  * Creates a new ColorItem
20480  * @param {Object} config Configuration options
20481  */
20482 Roo.menu.ColorItem = function(config){
20483     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20484     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20485     this.palette = this.component;
20486     this.relayEvents(this.palette, ["select"]);
20487     if(this.selectHandler){
20488         this.on('select', this.selectHandler, this.scope);
20489     }
20490 };
20491 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20492  * Based on:
20493  * Ext JS Library 1.1.1
20494  * Copyright(c) 2006-2007, Ext JS, LLC.
20495  *
20496  * Originally Released Under LGPL - original licence link has changed is not relivant.
20497  *
20498  * Fork - LGPL
20499  * <script type="text/javascript">
20500  */
20501  
20502
20503 /**
20504  * @class Roo.menu.DateMenu
20505  * @extends Roo.menu.Menu
20506  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20507  * @constructor
20508  * Creates a new DateMenu
20509  * @param {Object} config Configuration options
20510  */
20511 Roo.menu.DateMenu = function(config){
20512     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20513     this.plain = true;
20514     var di = new Roo.menu.DateItem(config);
20515     this.add(di);
20516     /**
20517      * The {@link Roo.DatePicker} instance for this DateMenu
20518      * @type DatePicker
20519      */
20520     this.picker = di.picker;
20521     /**
20522      * @event select
20523      * @param {DatePicker} picker
20524      * @param {Date} date
20525      */
20526     this.relayEvents(di, ["select"]);
20527     this.on('beforeshow', function(){
20528         if(this.picker){
20529             this.picker.hideMonthPicker(false);
20530         }
20531     }, this);
20532 };
20533 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20534     cls:'x-date-menu'
20535 });/*
20536  * Based on:
20537  * Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  *
20540  * Originally Released Under LGPL - original licence link has changed is not relivant.
20541  *
20542  * Fork - LGPL
20543  * <script type="text/javascript">
20544  */
20545  
20546
20547 /**
20548  * @class Roo.menu.ColorMenu
20549  * @extends Roo.menu.Menu
20550  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20551  * @constructor
20552  * Creates a new ColorMenu
20553  * @param {Object} config Configuration options
20554  */
20555 Roo.menu.ColorMenu = function(config){
20556     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20557     this.plain = true;
20558     var ci = new Roo.menu.ColorItem(config);
20559     this.add(ci);
20560     /**
20561      * The {@link Roo.ColorPalette} instance for this ColorMenu
20562      * @type ColorPalette
20563      */
20564     this.palette = ci.palette;
20565     /**
20566      * @event select
20567      * @param {ColorPalette} palette
20568      * @param {String} color
20569      */
20570     this.relayEvents(ci, ["select"]);
20571 };
20572 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20573  * Based on:
20574  * Ext JS Library 1.1.1
20575  * Copyright(c) 2006-2007, Ext JS, LLC.
20576  *
20577  * Originally Released Under LGPL - original licence link has changed is not relivant.
20578  *
20579  * Fork - LGPL
20580  * <script type="text/javascript">
20581  */
20582  
20583 /**
20584  * @class Roo.form.Field
20585  * @extends Roo.BoxComponent
20586  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20587  * @constructor
20588  * Creates a new Field
20589  * @param {Object} config Configuration options
20590  */
20591 Roo.form.Field = function(config){
20592     Roo.form.Field.superclass.constructor.call(this, config);
20593 };
20594
20595 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20596     /**
20597      * @cfg {String} fieldLabel Label to use when rendering a form.
20598      */
20599        /**
20600      * @cfg {String} qtip Mouse over tip
20601      */
20602      
20603     /**
20604      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20605      */
20606     invalidClass : "x-form-invalid",
20607     /**
20608      * @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")
20609      */
20610     invalidText : "The value in this field is invalid",
20611     /**
20612      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20613      */
20614     focusClass : "x-form-focus",
20615     /**
20616      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20617       automatic validation (defaults to "keyup").
20618      */
20619     validationEvent : "keyup",
20620     /**
20621      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20622      */
20623     validateOnBlur : true,
20624     /**
20625      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20626      */
20627     validationDelay : 250,
20628     /**
20629      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20630      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20631      */
20632     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20633     /**
20634      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20635      */
20636     fieldClass : "x-form-field",
20637     /**
20638      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20639      *<pre>
20640 Value         Description
20641 -----------   ----------------------------------------------------------------------
20642 qtip          Display a quick tip when the user hovers over the field
20643 title         Display a default browser title attribute popup
20644 under         Add a block div beneath the field containing the error text
20645 side          Add an error icon to the right of the field with a popup on hover
20646 [element id]  Add the error text directly to the innerHTML of the specified element
20647 </pre>
20648      */
20649     msgTarget : 'qtip',
20650     /**
20651      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20652      */
20653     msgFx : 'normal',
20654
20655     /**
20656      * @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.
20657      */
20658     readOnly : false,
20659
20660     /**
20661      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20662      */
20663     disabled : false,
20664
20665     /**
20666      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20667      */
20668     inputType : undefined,
20669     
20670     /**
20671      * @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).
20672          */
20673         tabIndex : undefined,
20674         
20675     // private
20676     isFormField : true,
20677
20678     // private
20679     hasFocus : false,
20680     /**
20681      * @property {Roo.Element} fieldEl
20682      * Element Containing the rendered Field (with label etc.)
20683      */
20684     /**
20685      * @cfg {Mixed} value A value to initialize this field with.
20686      */
20687     value : undefined,
20688
20689     /**
20690      * @cfg {String} name The field's HTML name attribute.
20691      */
20692     /**
20693      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20694      */
20695
20696         // private ??
20697         initComponent : function(){
20698         Roo.form.Field.superclass.initComponent.call(this);
20699         this.addEvents({
20700             /**
20701              * @event focus
20702              * Fires when this field receives input focus.
20703              * @param {Roo.form.Field} this
20704              */
20705             focus : true,
20706             /**
20707              * @event blur
20708              * Fires when this field loses input focus.
20709              * @param {Roo.form.Field} this
20710              */
20711             blur : true,
20712             /**
20713              * @event specialkey
20714              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20715              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20716              * @param {Roo.form.Field} this
20717              * @param {Roo.EventObject} e The event object
20718              */
20719             specialkey : true,
20720             /**
20721              * @event change
20722              * Fires just before the field blurs if the field value has changed.
20723              * @param {Roo.form.Field} this
20724              * @param {Mixed} newValue The new value
20725              * @param {Mixed} oldValue The original value
20726              */
20727             change : true,
20728             /**
20729              * @event invalid
20730              * Fires after the field has been marked as invalid.
20731              * @param {Roo.form.Field} this
20732              * @param {String} msg The validation message
20733              */
20734             invalid : true,
20735             /**
20736              * @event valid
20737              * Fires after the field has been validated with no errors.
20738              * @param {Roo.form.Field} this
20739              */
20740             valid : true,
20741              /**
20742              * @event keyup
20743              * Fires after the key up
20744              * @param {Roo.form.Field} this
20745              * @param {Roo.EventObject}  e The event Object
20746              */
20747             keyup : true
20748         });
20749     },
20750
20751     /**
20752      * Returns the name attribute of the field if available
20753      * @return {String} name The field name
20754      */
20755     getName: function(){
20756          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20757     },
20758
20759     // private
20760     onRender : function(ct, position){
20761         Roo.form.Field.superclass.onRender.call(this, ct, position);
20762         if(!this.el){
20763             var cfg = this.getAutoCreate();
20764             if(!cfg.name){
20765                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20766             }
20767             if (!cfg.name.length) {
20768                 delete cfg.name;
20769             }
20770             if(this.inputType){
20771                 cfg.type = this.inputType;
20772             }
20773             this.el = ct.createChild(cfg, position);
20774         }
20775         var type = this.el.dom.type;
20776         if(type){
20777             if(type == 'password'){
20778                 type = 'text';
20779             }
20780             this.el.addClass('x-form-'+type);
20781         }
20782         if(this.readOnly){
20783             this.el.dom.readOnly = true;
20784         }
20785         if(this.tabIndex !== undefined){
20786             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20787         }
20788
20789         this.el.addClass([this.fieldClass, this.cls]);
20790         this.initValue();
20791     },
20792
20793     /**
20794      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20795      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20796      * @return {Roo.form.Field} this
20797      */
20798     applyTo : function(target){
20799         this.allowDomMove = false;
20800         this.el = Roo.get(target);
20801         this.render(this.el.dom.parentNode);
20802         return this;
20803     },
20804
20805     // private
20806     initValue : function(){
20807         if(this.value !== undefined){
20808             this.setValue(this.value);
20809         }else if(this.el.dom.value.length > 0){
20810             this.setValue(this.el.dom.value);
20811         }
20812     },
20813
20814     /**
20815      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20816      */
20817     isDirty : function() {
20818         if(this.disabled) {
20819             return false;
20820         }
20821         return String(this.getValue()) !== String(this.originalValue);
20822     },
20823
20824     // private
20825     afterRender : function(){
20826         Roo.form.Field.superclass.afterRender.call(this);
20827         this.initEvents();
20828     },
20829
20830     // private
20831     fireKey : function(e){
20832         //Roo.log('field ' + e.getKey());
20833         if(e.isNavKeyPress()){
20834             this.fireEvent("specialkey", this, e);
20835         }
20836     },
20837
20838     /**
20839      * Resets the current field value to the originally loaded value and clears any validation messages
20840      */
20841     reset : function(){
20842         this.setValue(this.resetValue);
20843         this.clearInvalid();
20844     },
20845
20846     // private
20847     initEvents : function(){
20848         // safari killled keypress - so keydown is now used..
20849         this.el.on("keydown" , this.fireKey,  this);
20850         this.el.on("focus", this.onFocus,  this);
20851         this.el.on("blur", this.onBlur,  this);
20852         this.el.relayEvent('keyup', this);
20853
20854         // reference to original value for reset
20855         this.originalValue = this.getValue();
20856         this.resetValue =  this.getValue();
20857     },
20858
20859     // private
20860     onFocus : function(){
20861         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20862             this.el.addClass(this.focusClass);
20863         }
20864         if(!this.hasFocus){
20865             this.hasFocus = true;
20866             this.startValue = this.getValue();
20867             this.fireEvent("focus", this);
20868         }
20869     },
20870
20871     beforeBlur : Roo.emptyFn,
20872
20873     // private
20874     onBlur : function(){
20875         this.beforeBlur();
20876         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20877             this.el.removeClass(this.focusClass);
20878         }
20879         this.hasFocus = false;
20880         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20881             this.validate();
20882         }
20883         var v = this.getValue();
20884         if(String(v) !== String(this.startValue)){
20885             this.fireEvent('change', this, v, this.startValue);
20886         }
20887         this.fireEvent("blur", this);
20888     },
20889
20890     /**
20891      * Returns whether or not the field value is currently valid
20892      * @param {Boolean} preventMark True to disable marking the field invalid
20893      * @return {Boolean} True if the value is valid, else false
20894      */
20895     isValid : function(preventMark){
20896         if(this.disabled){
20897             return true;
20898         }
20899         var restore = this.preventMark;
20900         this.preventMark = preventMark === true;
20901         var v = this.validateValue(this.processValue(this.getRawValue()));
20902         this.preventMark = restore;
20903         return v;
20904     },
20905
20906     /**
20907      * Validates the field value
20908      * @return {Boolean} True if the value is valid, else false
20909      */
20910     validate : function(){
20911         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20912             this.clearInvalid();
20913             return true;
20914         }
20915         return false;
20916     },
20917
20918     processValue : function(value){
20919         return value;
20920     },
20921
20922     // private
20923     // Subclasses should provide the validation implementation by overriding this
20924     validateValue : function(value){
20925         return true;
20926     },
20927
20928     /**
20929      * Mark this field as invalid
20930      * @param {String} msg The validation message
20931      */
20932     markInvalid : function(msg){
20933         if(!this.rendered || this.preventMark){ // not rendered
20934             return;
20935         }
20936         
20937         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20938         
20939         obj.el.addClass(this.invalidClass);
20940         msg = msg || this.invalidText;
20941         switch(this.msgTarget){
20942             case 'qtip':
20943                 obj.el.dom.qtip = msg;
20944                 obj.el.dom.qclass = 'x-form-invalid-tip';
20945                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20946                     Roo.QuickTips.enable();
20947                 }
20948                 break;
20949             case 'title':
20950                 this.el.dom.title = msg;
20951                 break;
20952             case 'under':
20953                 if(!this.errorEl){
20954                     var elp = this.el.findParent('.x-form-element', 5, true);
20955                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20956                     this.errorEl.setWidth(elp.getWidth(true)-20);
20957                 }
20958                 this.errorEl.update(msg);
20959                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20960                 break;
20961             case 'side':
20962                 if(!this.errorIcon){
20963                     var elp = this.el.findParent('.x-form-element', 5, true);
20964                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20965                 }
20966                 this.alignErrorIcon();
20967                 this.errorIcon.dom.qtip = msg;
20968                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20969                 this.errorIcon.show();
20970                 this.on('resize', this.alignErrorIcon, this);
20971                 break;
20972             default:
20973                 var t = Roo.getDom(this.msgTarget);
20974                 t.innerHTML = msg;
20975                 t.style.display = this.msgDisplay;
20976                 break;
20977         }
20978         this.fireEvent('invalid', this, msg);
20979     },
20980
20981     // private
20982     alignErrorIcon : function(){
20983         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20984     },
20985
20986     /**
20987      * Clear any invalid styles/messages for this field
20988      */
20989     clearInvalid : function(){
20990         if(!this.rendered || this.preventMark){ // not rendered
20991             return;
20992         }
20993         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20994         
20995         obj.el.removeClass(this.invalidClass);
20996         switch(this.msgTarget){
20997             case 'qtip':
20998                 obj.el.dom.qtip = '';
20999                 break;
21000             case 'title':
21001                 this.el.dom.title = '';
21002                 break;
21003             case 'under':
21004                 if(this.errorEl){
21005                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21006                 }
21007                 break;
21008             case 'side':
21009                 if(this.errorIcon){
21010                     this.errorIcon.dom.qtip = '';
21011                     this.errorIcon.hide();
21012                     this.un('resize', this.alignErrorIcon, this);
21013                 }
21014                 break;
21015             default:
21016                 var t = Roo.getDom(this.msgTarget);
21017                 t.innerHTML = '';
21018                 t.style.display = 'none';
21019                 break;
21020         }
21021         this.fireEvent('valid', this);
21022     },
21023
21024     /**
21025      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21026      * @return {Mixed} value The field value
21027      */
21028     getRawValue : function(){
21029         var v = this.el.getValue();
21030         
21031         return v;
21032     },
21033
21034     /**
21035      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21036      * @return {Mixed} value The field value
21037      */
21038     getValue : function(){
21039         var v = this.el.getValue();
21040          
21041         return v;
21042     },
21043
21044     /**
21045      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21046      * @param {Mixed} value The value to set
21047      */
21048     setRawValue : function(v){
21049         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21050     },
21051
21052     /**
21053      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21054      * @param {Mixed} value The value to set
21055      */
21056     setValue : function(v){
21057         this.value = v;
21058         if(this.rendered){
21059             this.el.dom.value = (v === null || v === undefined ? '' : v);
21060              this.validate();
21061         }
21062     },
21063
21064     adjustSize : function(w, h){
21065         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21066         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21067         return s;
21068     },
21069
21070     adjustWidth : function(tag, w){
21071         tag = tag.toLowerCase();
21072         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21073             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21074                 if(tag == 'input'){
21075                     return w + 2;
21076                 }
21077                 if(tag == 'textarea'){
21078                     return w-2;
21079                 }
21080             }else if(Roo.isOpera){
21081                 if(tag == 'input'){
21082                     return w + 2;
21083                 }
21084                 if(tag == 'textarea'){
21085                     return w-2;
21086                 }
21087             }
21088         }
21089         return w;
21090     }
21091 });
21092
21093
21094 // anything other than normal should be considered experimental
21095 Roo.form.Field.msgFx = {
21096     normal : {
21097         show: function(msgEl, f){
21098             msgEl.setDisplayed('block');
21099         },
21100
21101         hide : function(msgEl, f){
21102             msgEl.setDisplayed(false).update('');
21103         }
21104     },
21105
21106     slide : {
21107         show: function(msgEl, f){
21108             msgEl.slideIn('t', {stopFx:true});
21109         },
21110
21111         hide : function(msgEl, f){
21112             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21113         }
21114     },
21115
21116     slideRight : {
21117         show: function(msgEl, f){
21118             msgEl.fixDisplay();
21119             msgEl.alignTo(f.el, 'tl-tr');
21120             msgEl.slideIn('l', {stopFx:true});
21121         },
21122
21123         hide : function(msgEl, f){
21124             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21125         }
21126     }
21127 };/*
21128  * Based on:
21129  * Ext JS Library 1.1.1
21130  * Copyright(c) 2006-2007, Ext JS, LLC.
21131  *
21132  * Originally Released Under LGPL - original licence link has changed is not relivant.
21133  *
21134  * Fork - LGPL
21135  * <script type="text/javascript">
21136  */
21137  
21138
21139 /**
21140  * @class Roo.form.TextField
21141  * @extends Roo.form.Field
21142  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21143  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21144  * @constructor
21145  * Creates a new TextField
21146  * @param {Object} config Configuration options
21147  */
21148 Roo.form.TextField = function(config){
21149     Roo.form.TextField.superclass.constructor.call(this, config);
21150     this.addEvents({
21151         /**
21152          * @event autosize
21153          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21154          * according to the default logic, but this event provides a hook for the developer to apply additional
21155          * logic at runtime to resize the field if needed.
21156              * @param {Roo.form.Field} this This text field
21157              * @param {Number} width The new field width
21158              */
21159         autosize : true
21160     });
21161 };
21162
21163 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21164     /**
21165      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21166      */
21167     grow : false,
21168     /**
21169      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21170      */
21171     growMin : 30,
21172     /**
21173      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21174      */
21175     growMax : 800,
21176     /**
21177      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21178      */
21179     vtype : null,
21180     /**
21181      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21182      */
21183     maskRe : null,
21184     /**
21185      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21186      */
21187     disableKeyFilter : false,
21188     /**
21189      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21190      */
21191     allowBlank : true,
21192     /**
21193      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21194      */
21195     minLength : 0,
21196     /**
21197      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21198      */
21199     maxLength : Number.MAX_VALUE,
21200     /**
21201      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21202      */
21203     minLengthText : "The minimum length for this field is {0}",
21204     /**
21205      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21206      */
21207     maxLengthText : "The maximum length for this field is {0}",
21208     /**
21209      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21210      */
21211     selectOnFocus : false,
21212     /**
21213      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21214      */
21215     blankText : "This field is required",
21216     /**
21217      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21218      * If available, this function will be called only after the basic validators all return true, and will be passed the
21219      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21220      */
21221     validator : null,
21222     /**
21223      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21224      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21225      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21226      */
21227     regex : null,
21228     /**
21229      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21230      */
21231     regexText : "",
21232     /**
21233      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21234      */
21235     emptyText : null,
21236    
21237
21238     // private
21239     initEvents : function()
21240     {
21241         if (this.emptyText) {
21242             this.el.attr('placeholder', this.emptyText);
21243         }
21244         
21245         Roo.form.TextField.superclass.initEvents.call(this);
21246         if(this.validationEvent == 'keyup'){
21247             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21248             this.el.on('keyup', this.filterValidation, this);
21249         }
21250         else if(this.validationEvent !== false){
21251             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21252         }
21253         
21254         if(this.selectOnFocus){
21255             this.on("focus", this.preFocus, this);
21256             
21257         }
21258         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21259             this.el.on("keypress", this.filterKeys, this);
21260         }
21261         if(this.grow){
21262             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21263             this.el.on("click", this.autoSize,  this);
21264         }
21265         if(this.el.is('input[type=password]') && Roo.isSafari){
21266             this.el.on('keydown', this.SafariOnKeyDown, this);
21267         }
21268     },
21269
21270     processValue : function(value){
21271         if(this.stripCharsRe){
21272             var newValue = value.replace(this.stripCharsRe, '');
21273             if(newValue !== value){
21274                 this.setRawValue(newValue);
21275                 return newValue;
21276             }
21277         }
21278         return value;
21279     },
21280
21281     filterValidation : function(e){
21282         if(!e.isNavKeyPress()){
21283             this.validationTask.delay(this.validationDelay);
21284         }
21285     },
21286
21287     // private
21288     onKeyUp : function(e){
21289         if(!e.isNavKeyPress()){
21290             this.autoSize();
21291         }
21292     },
21293
21294     /**
21295      * Resets the current field value to the originally-loaded value and clears any validation messages.
21296      *  
21297      */
21298     reset : function(){
21299         Roo.form.TextField.superclass.reset.call(this);
21300        
21301     },
21302
21303     
21304     // private
21305     preFocus : function(){
21306         
21307         if(this.selectOnFocus){
21308             this.el.dom.select();
21309         }
21310     },
21311
21312     
21313     // private
21314     filterKeys : function(e){
21315         var k = e.getKey();
21316         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21317             return;
21318         }
21319         var c = e.getCharCode(), cc = String.fromCharCode(c);
21320         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21321             return;
21322         }
21323         if(!this.maskRe.test(cc)){
21324             e.stopEvent();
21325         }
21326     },
21327
21328     setValue : function(v){
21329         
21330         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21331         
21332         this.autoSize();
21333     },
21334
21335     /**
21336      * Validates a value according to the field's validation rules and marks the field as invalid
21337      * if the validation fails
21338      * @param {Mixed} value The value to validate
21339      * @return {Boolean} True if the value is valid, else false
21340      */
21341     validateValue : function(value){
21342         if(value.length < 1)  { // if it's blank
21343              if(this.allowBlank){
21344                 this.clearInvalid();
21345                 return true;
21346              }else{
21347                 this.markInvalid(this.blankText);
21348                 return false;
21349              }
21350         }
21351         if(value.length < this.minLength){
21352             this.markInvalid(String.format(this.minLengthText, this.minLength));
21353             return false;
21354         }
21355         if(value.length > this.maxLength){
21356             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21357             return false;
21358         }
21359         if(this.vtype){
21360             var vt = Roo.form.VTypes;
21361             if(!vt[this.vtype](value, this)){
21362                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21363                 return false;
21364             }
21365         }
21366         if(typeof this.validator == "function"){
21367             var msg = this.validator(value);
21368             if(msg !== true){
21369                 this.markInvalid(msg);
21370                 return false;
21371             }
21372         }
21373         if(this.regex && !this.regex.test(value)){
21374             this.markInvalid(this.regexText);
21375             return false;
21376         }
21377         return true;
21378     },
21379
21380     /**
21381      * Selects text in this field
21382      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21383      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21384      */
21385     selectText : function(start, end){
21386         var v = this.getRawValue();
21387         if(v.length > 0){
21388             start = start === undefined ? 0 : start;
21389             end = end === undefined ? v.length : end;
21390             var d = this.el.dom;
21391             if(d.setSelectionRange){
21392                 d.setSelectionRange(start, end);
21393             }else if(d.createTextRange){
21394                 var range = d.createTextRange();
21395                 range.moveStart("character", start);
21396                 range.moveEnd("character", v.length-end);
21397                 range.select();
21398             }
21399         }
21400     },
21401
21402     /**
21403      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21404      * This only takes effect if grow = true, and fires the autosize event.
21405      */
21406     autoSize : function(){
21407         if(!this.grow || !this.rendered){
21408             return;
21409         }
21410         if(!this.metrics){
21411             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21412         }
21413         var el = this.el;
21414         var v = el.dom.value;
21415         var d = document.createElement('div');
21416         d.appendChild(document.createTextNode(v));
21417         v = d.innerHTML;
21418         d = null;
21419         v += "&#160;";
21420         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21421         this.el.setWidth(w);
21422         this.fireEvent("autosize", this, w);
21423     },
21424     
21425     // private
21426     SafariOnKeyDown : function(event)
21427     {
21428         // this is a workaround for a password hang bug on chrome/ webkit.
21429         
21430         var isSelectAll = false;
21431         
21432         if(this.el.dom.selectionEnd > 0){
21433             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21434         }
21435         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21436             event.preventDefault();
21437             this.setValue('');
21438             return;
21439         }
21440         
21441         if(isSelectAll){ // backspace and delete key
21442             
21443             event.preventDefault();
21444             // this is very hacky as keydown always get's upper case.
21445             //
21446             var cc = String.fromCharCode(event.getCharCode());
21447             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21448             
21449         }
21450         
21451         
21452     }
21453 });/*
21454  * Based on:
21455  * Ext JS Library 1.1.1
21456  * Copyright(c) 2006-2007, Ext JS, LLC.
21457  *
21458  * Originally Released Under LGPL - original licence link has changed is not relivant.
21459  *
21460  * Fork - LGPL
21461  * <script type="text/javascript">
21462  */
21463  
21464 /**
21465  * @class Roo.form.Hidden
21466  * @extends Roo.form.TextField
21467  * Simple Hidden element used on forms 
21468  * 
21469  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21470  * 
21471  * @constructor
21472  * Creates a new Hidden form element.
21473  * @param {Object} config Configuration options
21474  */
21475
21476
21477
21478 // easy hidden field...
21479 Roo.form.Hidden = function(config){
21480     Roo.form.Hidden.superclass.constructor.call(this, config);
21481 };
21482   
21483 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21484     fieldLabel:      '',
21485     inputType:      'hidden',
21486     width:          50,
21487     allowBlank:     true,
21488     labelSeparator: '',
21489     hidden:         true,
21490     itemCls :       'x-form-item-display-none'
21491
21492
21493 });
21494
21495
21496 /*
21497  * Based on:
21498  * Ext JS Library 1.1.1
21499  * Copyright(c) 2006-2007, Ext JS, LLC.
21500  *
21501  * Originally Released Under LGPL - original licence link has changed is not relivant.
21502  *
21503  * Fork - LGPL
21504  * <script type="text/javascript">
21505  */
21506  
21507 /**
21508  * @class Roo.form.TriggerField
21509  * @extends Roo.form.TextField
21510  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21511  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21512  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21513  * for which you can provide a custom implementation.  For example:
21514  * <pre><code>
21515 var trigger = new Roo.form.TriggerField();
21516 trigger.onTriggerClick = myTriggerFn;
21517 trigger.applyTo('my-field');
21518 </code></pre>
21519  *
21520  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21521  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21522  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21523  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21524  * @constructor
21525  * Create a new TriggerField.
21526  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21527  * to the base TextField)
21528  */
21529 Roo.form.TriggerField = function(config){
21530     this.mimicing = false;
21531     Roo.form.TriggerField.superclass.constructor.call(this, config);
21532 };
21533
21534 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21535     /**
21536      * @cfg {String} triggerClass A CSS class to apply to the trigger
21537      */
21538     /**
21539      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21540      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21541      */
21542     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21543     /**
21544      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21545      */
21546     hideTrigger:false,
21547
21548     /** @cfg {Boolean} grow @hide */
21549     /** @cfg {Number} growMin @hide */
21550     /** @cfg {Number} growMax @hide */
21551
21552     /**
21553      * @hide 
21554      * @method
21555      */
21556     autoSize: Roo.emptyFn,
21557     // private
21558     monitorTab : true,
21559     // private
21560     deferHeight : true,
21561
21562     
21563     actionMode : 'wrap',
21564     // private
21565     onResize : function(w, h){
21566         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21567         if(typeof w == 'number'){
21568             var x = w - this.trigger.getWidth();
21569             this.el.setWidth(this.adjustWidth('input', x));
21570             this.trigger.setStyle('left', x+'px');
21571         }
21572     },
21573
21574     // private
21575     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21576
21577     // private
21578     getResizeEl : function(){
21579         return this.wrap;
21580     },
21581
21582     // private
21583     getPositionEl : function(){
21584         return this.wrap;
21585     },
21586
21587     // private
21588     alignErrorIcon : function(){
21589         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21590     },
21591
21592     // private
21593     onRender : function(ct, position){
21594         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21595         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21596         this.trigger = this.wrap.createChild(this.triggerConfig ||
21597                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21598         if(this.hideTrigger){
21599             this.trigger.setDisplayed(false);
21600         }
21601         this.initTrigger();
21602         if(!this.width){
21603             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21604         }
21605     },
21606
21607     // private
21608     initTrigger : function(){
21609         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21610         this.trigger.addClassOnOver('x-form-trigger-over');
21611         this.trigger.addClassOnClick('x-form-trigger-click');
21612     },
21613
21614     // private
21615     onDestroy : function(){
21616         if(this.trigger){
21617             this.trigger.removeAllListeners();
21618             this.trigger.remove();
21619         }
21620         if(this.wrap){
21621             this.wrap.remove();
21622         }
21623         Roo.form.TriggerField.superclass.onDestroy.call(this);
21624     },
21625
21626     // private
21627     onFocus : function(){
21628         Roo.form.TriggerField.superclass.onFocus.call(this);
21629         if(!this.mimicing){
21630             this.wrap.addClass('x-trigger-wrap-focus');
21631             this.mimicing = true;
21632             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21633             if(this.monitorTab){
21634                 this.el.on("keydown", this.checkTab, this);
21635             }
21636         }
21637     },
21638
21639     // private
21640     checkTab : function(e){
21641         if(e.getKey() == e.TAB){
21642             this.triggerBlur();
21643         }
21644     },
21645
21646     // private
21647     onBlur : function(){
21648         // do nothing
21649     },
21650
21651     // private
21652     mimicBlur : function(e, t){
21653         if(!this.wrap.contains(t) && this.validateBlur()){
21654             this.triggerBlur();
21655         }
21656     },
21657
21658     // private
21659     triggerBlur : function(){
21660         this.mimicing = false;
21661         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21662         if(this.monitorTab){
21663             this.el.un("keydown", this.checkTab, this);
21664         }
21665         this.wrap.removeClass('x-trigger-wrap-focus');
21666         Roo.form.TriggerField.superclass.onBlur.call(this);
21667     },
21668
21669     // private
21670     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21671     validateBlur : function(e, t){
21672         return true;
21673     },
21674
21675     // private
21676     onDisable : function(){
21677         Roo.form.TriggerField.superclass.onDisable.call(this);
21678         if(this.wrap){
21679             this.wrap.addClass('x-item-disabled');
21680         }
21681     },
21682
21683     // private
21684     onEnable : function(){
21685         Roo.form.TriggerField.superclass.onEnable.call(this);
21686         if(this.wrap){
21687             this.wrap.removeClass('x-item-disabled');
21688         }
21689     },
21690
21691     // private
21692     onShow : function(){
21693         var ae = this.getActionEl();
21694         
21695         if(ae){
21696             ae.dom.style.display = '';
21697             ae.dom.style.visibility = 'visible';
21698         }
21699     },
21700
21701     // private
21702     
21703     onHide : function(){
21704         var ae = this.getActionEl();
21705         ae.dom.style.display = 'none';
21706     },
21707
21708     /**
21709      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21710      * by an implementing function.
21711      * @method
21712      * @param {EventObject} e
21713      */
21714     onTriggerClick : Roo.emptyFn
21715 });
21716
21717 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21718 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21719 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21720 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21721     initComponent : function(){
21722         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21723
21724         this.triggerConfig = {
21725             tag:'span', cls:'x-form-twin-triggers', cn:[
21726             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21727             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21728         ]};
21729     },
21730
21731     getTrigger : function(index){
21732         return this.triggers[index];
21733     },
21734
21735     initTrigger : function(){
21736         var ts = this.trigger.select('.x-form-trigger', true);
21737         this.wrap.setStyle('overflow', 'hidden');
21738         var triggerField = this;
21739         ts.each(function(t, all, index){
21740             t.hide = function(){
21741                 var w = triggerField.wrap.getWidth();
21742                 this.dom.style.display = 'none';
21743                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21744             };
21745             t.show = function(){
21746                 var w = triggerField.wrap.getWidth();
21747                 this.dom.style.display = '';
21748                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21749             };
21750             var triggerIndex = 'Trigger'+(index+1);
21751
21752             if(this['hide'+triggerIndex]){
21753                 t.dom.style.display = 'none';
21754             }
21755             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21756             t.addClassOnOver('x-form-trigger-over');
21757             t.addClassOnClick('x-form-trigger-click');
21758         }, this);
21759         this.triggers = ts.elements;
21760     },
21761
21762     onTrigger1Click : Roo.emptyFn,
21763     onTrigger2Click : Roo.emptyFn
21764 });/*
21765  * Based on:
21766  * Ext JS Library 1.1.1
21767  * Copyright(c) 2006-2007, Ext JS, LLC.
21768  *
21769  * Originally Released Under LGPL - original licence link has changed is not relivant.
21770  *
21771  * Fork - LGPL
21772  * <script type="text/javascript">
21773  */
21774  
21775 /**
21776  * @class Roo.form.TextArea
21777  * @extends Roo.form.TextField
21778  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21779  * support for auto-sizing.
21780  * @constructor
21781  * Creates a new TextArea
21782  * @param {Object} config Configuration options
21783  */
21784 Roo.form.TextArea = function(config){
21785     Roo.form.TextArea.superclass.constructor.call(this, config);
21786     // these are provided exchanges for backwards compat
21787     // minHeight/maxHeight were replaced by growMin/growMax to be
21788     // compatible with TextField growing config values
21789     if(this.minHeight !== undefined){
21790         this.growMin = this.minHeight;
21791     }
21792     if(this.maxHeight !== undefined){
21793         this.growMax = this.maxHeight;
21794     }
21795 };
21796
21797 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21798     /**
21799      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21800      */
21801     growMin : 60,
21802     /**
21803      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21804      */
21805     growMax: 1000,
21806     /**
21807      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21808      * in the field (equivalent to setting overflow: hidden, defaults to false)
21809      */
21810     preventScrollbars: false,
21811     /**
21812      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21813      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21814      */
21815
21816     // private
21817     onRender : function(ct, position){
21818         if(!this.el){
21819             this.defaultAutoCreate = {
21820                 tag: "textarea",
21821                 style:"width:300px;height:60px;",
21822                 autocomplete: "off"
21823             };
21824         }
21825         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21826         if(this.grow){
21827             this.textSizeEl = Roo.DomHelper.append(document.body, {
21828                 tag: "pre", cls: "x-form-grow-sizer"
21829             });
21830             if(this.preventScrollbars){
21831                 this.el.setStyle("overflow", "hidden");
21832             }
21833             this.el.setHeight(this.growMin);
21834         }
21835     },
21836
21837     onDestroy : function(){
21838         if(this.textSizeEl){
21839             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21840         }
21841         Roo.form.TextArea.superclass.onDestroy.call(this);
21842     },
21843
21844     // private
21845     onKeyUp : function(e){
21846         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21847             this.autoSize();
21848         }
21849     },
21850
21851     /**
21852      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21853      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21854      */
21855     autoSize : function(){
21856         if(!this.grow || !this.textSizeEl){
21857             return;
21858         }
21859         var el = this.el;
21860         var v = el.dom.value;
21861         var ts = this.textSizeEl;
21862
21863         ts.innerHTML = '';
21864         ts.appendChild(document.createTextNode(v));
21865         v = ts.innerHTML;
21866
21867         Roo.fly(ts).setWidth(this.el.getWidth());
21868         if(v.length < 1){
21869             v = "&#160;&#160;";
21870         }else{
21871             if(Roo.isIE){
21872                 v = v.replace(/\n/g, '<p>&#160;</p>');
21873             }
21874             v += "&#160;\n&#160;";
21875         }
21876         ts.innerHTML = v;
21877         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21878         if(h != this.lastHeight){
21879             this.lastHeight = h;
21880             this.el.setHeight(h);
21881             this.fireEvent("autosize", this, h);
21882         }
21883     }
21884 });/*
21885  * Based on:
21886  * Ext JS Library 1.1.1
21887  * Copyright(c) 2006-2007, Ext JS, LLC.
21888  *
21889  * Originally Released Under LGPL - original licence link has changed is not relivant.
21890  *
21891  * Fork - LGPL
21892  * <script type="text/javascript">
21893  */
21894  
21895
21896 /**
21897  * @class Roo.form.NumberField
21898  * @extends Roo.form.TextField
21899  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21900  * @constructor
21901  * Creates a new NumberField
21902  * @param {Object} config Configuration options
21903  */
21904 Roo.form.NumberField = function(config){
21905     Roo.form.NumberField.superclass.constructor.call(this, config);
21906 };
21907
21908 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21909     /**
21910      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21911      */
21912     fieldClass: "x-form-field x-form-num-field",
21913     /**
21914      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21915      */
21916     allowDecimals : true,
21917     /**
21918      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21919      */
21920     decimalSeparator : ".",
21921     /**
21922      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21923      */
21924     decimalPrecision : 2,
21925     /**
21926      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21927      */
21928     allowNegative : true,
21929     /**
21930      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21931      */
21932     minValue : Number.NEGATIVE_INFINITY,
21933     /**
21934      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21935      */
21936     maxValue : Number.MAX_VALUE,
21937     /**
21938      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21939      */
21940     minText : "The minimum value for this field is {0}",
21941     /**
21942      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21943      */
21944     maxText : "The maximum value for this field is {0}",
21945     /**
21946      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21947      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21948      */
21949     nanText : "{0} is not a valid number",
21950
21951     // private
21952     initEvents : function(){
21953         Roo.form.NumberField.superclass.initEvents.call(this);
21954         var allowed = "0123456789";
21955         if(this.allowDecimals){
21956             allowed += this.decimalSeparator;
21957         }
21958         if(this.allowNegative){
21959             allowed += "-";
21960         }
21961         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21962         var keyPress = function(e){
21963             var k = e.getKey();
21964             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21965                 return;
21966             }
21967             var c = e.getCharCode();
21968             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21969                 e.stopEvent();
21970             }
21971         };
21972         this.el.on("keypress", keyPress, this);
21973     },
21974
21975     // private
21976     validateValue : function(value){
21977         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21978             return false;
21979         }
21980         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21981              return true;
21982         }
21983         var num = this.parseValue(value);
21984         if(isNaN(num)){
21985             this.markInvalid(String.format(this.nanText, value));
21986             return false;
21987         }
21988         if(num < this.minValue){
21989             this.markInvalid(String.format(this.minText, this.minValue));
21990             return false;
21991         }
21992         if(num > this.maxValue){
21993             this.markInvalid(String.format(this.maxText, this.maxValue));
21994             return false;
21995         }
21996         return true;
21997     },
21998
21999     getValue : function(){
22000         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22001     },
22002
22003     // private
22004     parseValue : function(value){
22005         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22006         return isNaN(value) ? '' : value;
22007     },
22008
22009     // private
22010     fixPrecision : function(value){
22011         var nan = isNaN(value);
22012         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22013             return nan ? '' : value;
22014         }
22015         return parseFloat(value).toFixed(this.decimalPrecision);
22016     },
22017
22018     setValue : function(v){
22019         v = this.fixPrecision(v);
22020         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22021     },
22022
22023     // private
22024     decimalPrecisionFcn : function(v){
22025         return Math.floor(v);
22026     },
22027
22028     beforeBlur : function(){
22029         var v = this.parseValue(this.getRawValue());
22030         if(v){
22031             this.setValue(v);
22032         }
22033     }
22034 });/*
22035  * Based on:
22036  * Ext JS Library 1.1.1
22037  * Copyright(c) 2006-2007, Ext JS, LLC.
22038  *
22039  * Originally Released Under LGPL - original licence link has changed is not relivant.
22040  *
22041  * Fork - LGPL
22042  * <script type="text/javascript">
22043  */
22044  
22045 /**
22046  * @class Roo.form.DateField
22047  * @extends Roo.form.TriggerField
22048  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22049 * @constructor
22050 * Create a new DateField
22051 * @param {Object} config
22052  */
22053 Roo.form.DateField = function(config){
22054     Roo.form.DateField.superclass.constructor.call(this, config);
22055     
22056       this.addEvents({
22057          
22058         /**
22059          * @event select
22060          * Fires when a date is selected
22061              * @param {Roo.form.DateField} combo This combo box
22062              * @param {Date} date The date selected
22063              */
22064         'select' : true
22065          
22066     });
22067     
22068     
22069     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22070     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22071     this.ddMatch = null;
22072     if(this.disabledDates){
22073         var dd = this.disabledDates;
22074         var re = "(?:";
22075         for(var i = 0; i < dd.length; i++){
22076             re += dd[i];
22077             if(i != dd.length-1) re += "|";
22078         }
22079         this.ddMatch = new RegExp(re + ")");
22080     }
22081 };
22082
22083 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22084     /**
22085      * @cfg {String} format
22086      * The default date format string which can be overriden for localization support.  The format must be
22087      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22088      */
22089     format : "m/d/y",
22090     /**
22091      * @cfg {String} altFormats
22092      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22093      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22094      */
22095     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22096     /**
22097      * @cfg {Array} disabledDays
22098      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22099      */
22100     disabledDays : null,
22101     /**
22102      * @cfg {String} disabledDaysText
22103      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22104      */
22105     disabledDaysText : "Disabled",
22106     /**
22107      * @cfg {Array} disabledDates
22108      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22109      * expression so they are very powerful. Some examples:
22110      * <ul>
22111      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22112      * <li>["03/08", "09/16"] would disable those days for every year</li>
22113      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22114      * <li>["03/../2006"] would disable every day in March 2006</li>
22115      * <li>["^03"] would disable every day in every March</li>
22116      * </ul>
22117      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22118      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22119      */
22120     disabledDates : null,
22121     /**
22122      * @cfg {String} disabledDatesText
22123      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22124      */
22125     disabledDatesText : "Disabled",
22126     /**
22127      * @cfg {Date/String} minValue
22128      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22129      * valid format (defaults to null).
22130      */
22131     minValue : null,
22132     /**
22133      * @cfg {Date/String} maxValue
22134      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22135      * valid format (defaults to null).
22136      */
22137     maxValue : null,
22138     /**
22139      * @cfg {String} minText
22140      * The error text to display when the date in the cell is before minValue (defaults to
22141      * 'The date in this field must be after {minValue}').
22142      */
22143     minText : "The date in this field must be equal to or after {0}",
22144     /**
22145      * @cfg {String} maxText
22146      * The error text to display when the date in the cell is after maxValue (defaults to
22147      * 'The date in this field must be before {maxValue}').
22148      */
22149     maxText : "The date in this field must be equal to or before {0}",
22150     /**
22151      * @cfg {String} invalidText
22152      * The error text to display when the date in the field is invalid (defaults to
22153      * '{value} is not a valid date - it must be in the format {format}').
22154      */
22155     invalidText : "{0} is not a valid date - it must be in the format {1}",
22156     /**
22157      * @cfg {String} triggerClass
22158      * An additional CSS class used to style the trigger button.  The trigger will always get the
22159      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22160      * which displays a calendar icon).
22161      */
22162     triggerClass : 'x-form-date-trigger',
22163     
22164
22165     /**
22166      * @cfg {Boolean} useIso
22167      * if enabled, then the date field will use a hidden field to store the 
22168      * real value as iso formated date. default (false)
22169      */ 
22170     useIso : false,
22171     /**
22172      * @cfg {String/Object} autoCreate
22173      * A DomHelper element spec, or true for a default element spec (defaults to
22174      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22175      */ 
22176     // private
22177     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22178     
22179     // private
22180     hiddenField: false,
22181     
22182     onRender : function(ct, position)
22183     {
22184         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22185         if (this.useIso) {
22186             //this.el.dom.removeAttribute('name'); 
22187             Roo.log("Changing name?");
22188             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22189             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22190                     'before', true);
22191             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22192             // prevent input submission
22193             this.hiddenName = this.name;
22194         }
22195             
22196             
22197     },
22198     
22199     // private
22200     validateValue : function(value)
22201     {
22202         value = this.formatDate(value);
22203         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22204             Roo.log('super failed');
22205             return false;
22206         }
22207         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22208              return true;
22209         }
22210         var svalue = value;
22211         value = this.parseDate(value);
22212         if(!value){
22213             Roo.log('parse date failed' + svalue);
22214             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22215             return false;
22216         }
22217         var time = value.getTime();
22218         if(this.minValue && time < this.minValue.getTime()){
22219             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22220             return false;
22221         }
22222         if(this.maxValue && time > this.maxValue.getTime()){
22223             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22224             return false;
22225         }
22226         if(this.disabledDays){
22227             var day = value.getDay();
22228             for(var i = 0; i < this.disabledDays.length; i++) {
22229                 if(day === this.disabledDays[i]){
22230                     this.markInvalid(this.disabledDaysText);
22231                     return false;
22232                 }
22233             }
22234         }
22235         var fvalue = this.formatDate(value);
22236         if(this.ddMatch && this.ddMatch.test(fvalue)){
22237             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22238             return false;
22239         }
22240         return true;
22241     },
22242
22243     // private
22244     // Provides logic to override the default TriggerField.validateBlur which just returns true
22245     validateBlur : function(){
22246         return !this.menu || !this.menu.isVisible();
22247     },
22248     
22249     getName: function()
22250     {
22251         // returns hidden if it's set..
22252         if (!this.rendered) {return ''};
22253         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22254         
22255     },
22256
22257     /**
22258      * Returns the current date value of the date field.
22259      * @return {Date} The date value
22260      */
22261     getValue : function(){
22262         
22263         return  this.hiddenField ?
22264                 this.hiddenField.value :
22265                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22266     },
22267
22268     /**
22269      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22270      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22271      * (the default format used is "m/d/y").
22272      * <br />Usage:
22273      * <pre><code>
22274 //All of these calls set the same date value (May 4, 2006)
22275
22276 //Pass a date object:
22277 var dt = new Date('5/4/06');
22278 dateField.setValue(dt);
22279
22280 //Pass a date string (default format):
22281 dateField.setValue('5/4/06');
22282
22283 //Pass a date string (custom format):
22284 dateField.format = 'Y-m-d';
22285 dateField.setValue('2006-5-4');
22286 </code></pre>
22287      * @param {String/Date} date The date or valid date string
22288      */
22289     setValue : function(date){
22290         if (this.hiddenField) {
22291             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22292         }
22293         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22294         // make sure the value field is always stored as a date..
22295         this.value = this.parseDate(date);
22296         
22297         
22298     },
22299
22300     // private
22301     parseDate : function(value){
22302         if(!value || value instanceof Date){
22303             return value;
22304         }
22305         var v = Date.parseDate(value, this.format);
22306          if (!v && this.useIso) {
22307             v = Date.parseDate(value, 'Y-m-d');
22308         }
22309         if(!v && this.altFormats){
22310             if(!this.altFormatsArray){
22311                 this.altFormatsArray = this.altFormats.split("|");
22312             }
22313             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22314                 v = Date.parseDate(value, this.altFormatsArray[i]);
22315             }
22316         }
22317         return v;
22318     },
22319
22320     // private
22321     formatDate : function(date, fmt){
22322         return (!date || !(date instanceof Date)) ?
22323                date : date.dateFormat(fmt || this.format);
22324     },
22325
22326     // private
22327     menuListeners : {
22328         select: function(m, d){
22329             
22330             this.setValue(d);
22331             this.fireEvent('select', this, d);
22332         },
22333         show : function(){ // retain focus styling
22334             this.onFocus();
22335         },
22336         hide : function(){
22337             this.focus.defer(10, this);
22338             var ml = this.menuListeners;
22339             this.menu.un("select", ml.select,  this);
22340             this.menu.un("show", ml.show,  this);
22341             this.menu.un("hide", ml.hide,  this);
22342         }
22343     },
22344
22345     // private
22346     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22347     onTriggerClick : function(){
22348         if(this.disabled){
22349             return;
22350         }
22351         if(this.menu == null){
22352             this.menu = new Roo.menu.DateMenu();
22353         }
22354         Roo.apply(this.menu.picker,  {
22355             showClear: this.allowBlank,
22356             minDate : this.minValue,
22357             maxDate : this.maxValue,
22358             disabledDatesRE : this.ddMatch,
22359             disabledDatesText : this.disabledDatesText,
22360             disabledDays : this.disabledDays,
22361             disabledDaysText : this.disabledDaysText,
22362             format : this.useIso ? 'Y-m-d' : this.format,
22363             minText : String.format(this.minText, this.formatDate(this.minValue)),
22364             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22365         });
22366         this.menu.on(Roo.apply({}, this.menuListeners, {
22367             scope:this
22368         }));
22369         this.menu.picker.setValue(this.getValue() || new Date());
22370         this.menu.show(this.el, "tl-bl?");
22371     },
22372
22373     beforeBlur : function(){
22374         var v = this.parseDate(this.getRawValue());
22375         if(v){
22376             this.setValue(v);
22377         }
22378     },
22379
22380     /*@
22381      * overide
22382      * 
22383      */
22384     isDirty : function() {
22385         if(this.disabled) {
22386             return false;
22387         }
22388         
22389         if(typeof(this.startValue) === 'undefined'){
22390             return false;
22391         }
22392         
22393         return String(this.getValue()) !== String(this.startValue);
22394         
22395     }
22396 });/*
22397  * Based on:
22398  * Ext JS Library 1.1.1
22399  * Copyright(c) 2006-2007, Ext JS, LLC.
22400  *
22401  * Originally Released Under LGPL - original licence link has changed is not relivant.
22402  *
22403  * Fork - LGPL
22404  * <script type="text/javascript">
22405  */
22406  
22407 /**
22408  * @class Roo.form.MonthField
22409  * @extends Roo.form.TriggerField
22410  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22411 * @constructor
22412 * Create a new MonthField
22413 * @param {Object} config
22414  */
22415 Roo.form.MonthField = function(config){
22416     
22417     Roo.form.MonthField.superclass.constructor.call(this, config);
22418     
22419       this.addEvents({
22420          
22421         /**
22422          * @event select
22423          * Fires when a date is selected
22424              * @param {Roo.form.MonthFieeld} combo This combo box
22425              * @param {Date} date The date selected
22426              */
22427         'select' : true
22428          
22429     });
22430     
22431     
22432     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22433     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22434     this.ddMatch = null;
22435     if(this.disabledDates){
22436         var dd = this.disabledDates;
22437         var re = "(?:";
22438         for(var i = 0; i < dd.length; i++){
22439             re += dd[i];
22440             if(i != dd.length-1) re += "|";
22441         }
22442         this.ddMatch = new RegExp(re + ")");
22443     }
22444 };
22445
22446 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22447     /**
22448      * @cfg {String} format
22449      * The default date format string which can be overriden for localization support.  The format must be
22450      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22451      */
22452     format : "M Y",
22453     /**
22454      * @cfg {String} altFormats
22455      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22456      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22457      */
22458     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22459     /**
22460      * @cfg {Array} disabledDays
22461      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22462      */
22463     disabledDays : [0,1,2,3,4,5,6],
22464     /**
22465      * @cfg {String} disabledDaysText
22466      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22467      */
22468     disabledDaysText : "Disabled",
22469     /**
22470      * @cfg {Array} disabledDates
22471      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22472      * expression so they are very powerful. Some examples:
22473      * <ul>
22474      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22475      * <li>["03/08", "09/16"] would disable those days for every year</li>
22476      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22477      * <li>["03/../2006"] would disable every day in March 2006</li>
22478      * <li>["^03"] would disable every day in every March</li>
22479      * </ul>
22480      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22481      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22482      */
22483     disabledDates : null,
22484     /**
22485      * @cfg {String} disabledDatesText
22486      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22487      */
22488     disabledDatesText : "Disabled",
22489     /**
22490      * @cfg {Date/String} minValue
22491      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22492      * valid format (defaults to null).
22493      */
22494     minValue : null,
22495     /**
22496      * @cfg {Date/String} maxValue
22497      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22498      * valid format (defaults to null).
22499      */
22500     maxValue : null,
22501     /**
22502      * @cfg {String} minText
22503      * The error text to display when the date in the cell is before minValue (defaults to
22504      * 'The date in this field must be after {minValue}').
22505      */
22506     minText : "The date in this field must be equal to or after {0}",
22507     /**
22508      * @cfg {String} maxTextf
22509      * The error text to display when the date in the cell is after maxValue (defaults to
22510      * 'The date in this field must be before {maxValue}').
22511      */
22512     maxText : "The date in this field must be equal to or before {0}",
22513     /**
22514      * @cfg {String} invalidText
22515      * The error text to display when the date in the field is invalid (defaults to
22516      * '{value} is not a valid date - it must be in the format {format}').
22517      */
22518     invalidText : "{0} is not a valid date - it must be in the format {1}",
22519     /**
22520      * @cfg {String} triggerClass
22521      * An additional CSS class used to style the trigger button.  The trigger will always get the
22522      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22523      * which displays a calendar icon).
22524      */
22525     triggerClass : 'x-form-date-trigger',
22526     
22527
22528     /**
22529      * @cfg {Boolean} useIso
22530      * if enabled, then the date field will use a hidden field to store the 
22531      * real value as iso formated date. default (true)
22532      */ 
22533     useIso : true,
22534     /**
22535      * @cfg {String/Object} autoCreate
22536      * A DomHelper element spec, or true for a default element spec (defaults to
22537      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22538      */ 
22539     // private
22540     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22541     
22542     // private
22543     hiddenField: false,
22544     
22545     hideMonthPicker : false,
22546     
22547     onRender : function(ct, position)
22548     {
22549         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22550         if (this.useIso) {
22551             this.el.dom.removeAttribute('name'); 
22552             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22553                     'before', true);
22554             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22555             // prevent input submission
22556             this.hiddenName = this.name;
22557         }
22558             
22559             
22560     },
22561     
22562     // private
22563     validateValue : function(value)
22564     {
22565         value = this.formatDate(value);
22566         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22567             return false;
22568         }
22569         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22570              return true;
22571         }
22572         var svalue = value;
22573         value = this.parseDate(value);
22574         if(!value){
22575             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22576             return false;
22577         }
22578         var time = value.getTime();
22579         if(this.minValue && time < this.minValue.getTime()){
22580             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22581             return false;
22582         }
22583         if(this.maxValue && time > this.maxValue.getTime()){
22584             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22585             return false;
22586         }
22587         /*if(this.disabledDays){
22588             var day = value.getDay();
22589             for(var i = 0; i < this.disabledDays.length; i++) {
22590                 if(day === this.disabledDays[i]){
22591                     this.markInvalid(this.disabledDaysText);
22592                     return false;
22593                 }
22594             }
22595         }
22596         */
22597         var fvalue = this.formatDate(value);
22598         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22599             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22600             return false;
22601         }
22602         */
22603         return true;
22604     },
22605
22606     // private
22607     // Provides logic to override the default TriggerField.validateBlur which just returns true
22608     validateBlur : function(){
22609         return !this.menu || !this.menu.isVisible();
22610     },
22611
22612     /**
22613      * Returns the current date value of the date field.
22614      * @return {Date} The date value
22615      */
22616     getValue : function(){
22617         
22618         
22619         
22620         return  this.hiddenField ?
22621                 this.hiddenField.value :
22622                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22623     },
22624
22625     /**
22626      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22627      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22628      * (the default format used is "m/d/y").
22629      * <br />Usage:
22630      * <pre><code>
22631 //All of these calls set the same date value (May 4, 2006)
22632
22633 //Pass a date object:
22634 var dt = new Date('5/4/06');
22635 monthField.setValue(dt);
22636
22637 //Pass a date string (default format):
22638 monthField.setValue('5/4/06');
22639
22640 //Pass a date string (custom format):
22641 monthField.format = 'Y-m-d';
22642 monthField.setValue('2006-5-4');
22643 </code></pre>
22644      * @param {String/Date} date The date or valid date string
22645      */
22646     setValue : function(date){
22647         Roo.log('month setValue' + date);
22648         // can only be first of month..
22649         
22650         var val = this.parseDate(date);
22651         
22652         if (this.hiddenField) {
22653             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22654         }
22655         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22656         this.value = this.parseDate(date);
22657     },
22658
22659     // private
22660     parseDate : function(value){
22661         if(!value || value instanceof Date){
22662             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22663             return value;
22664         }
22665         var v = Date.parseDate(value, this.format);
22666         if (!v && this.useIso) {
22667             v = Date.parseDate(value, 'Y-m-d');
22668         }
22669         if (v) {
22670             // 
22671             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22672         }
22673         
22674         
22675         if(!v && this.altFormats){
22676             if(!this.altFormatsArray){
22677                 this.altFormatsArray = this.altFormats.split("|");
22678             }
22679             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22680                 v = Date.parseDate(value, this.altFormatsArray[i]);
22681             }
22682         }
22683         return v;
22684     },
22685
22686     // private
22687     formatDate : function(date, fmt){
22688         return (!date || !(date instanceof Date)) ?
22689                date : date.dateFormat(fmt || this.format);
22690     },
22691
22692     // private
22693     menuListeners : {
22694         select: function(m, d){
22695             this.setValue(d);
22696             this.fireEvent('select', this, d);
22697         },
22698         show : function(){ // retain focus styling
22699             this.onFocus();
22700         },
22701         hide : function(){
22702             this.focus.defer(10, this);
22703             var ml = this.menuListeners;
22704             this.menu.un("select", ml.select,  this);
22705             this.menu.un("show", ml.show,  this);
22706             this.menu.un("hide", ml.hide,  this);
22707         }
22708     },
22709     // private
22710     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22711     onTriggerClick : function(){
22712         if(this.disabled){
22713             return;
22714         }
22715         if(this.menu == null){
22716             this.menu = new Roo.menu.DateMenu();
22717            
22718         }
22719         
22720         Roo.apply(this.menu.picker,  {
22721             
22722             showClear: this.allowBlank,
22723             minDate : this.minValue,
22724             maxDate : this.maxValue,
22725             disabledDatesRE : this.ddMatch,
22726             disabledDatesText : this.disabledDatesText,
22727             
22728             format : this.useIso ? 'Y-m-d' : this.format,
22729             minText : String.format(this.minText, this.formatDate(this.minValue)),
22730             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22731             
22732         });
22733          this.menu.on(Roo.apply({}, this.menuListeners, {
22734             scope:this
22735         }));
22736        
22737         
22738         var m = this.menu;
22739         var p = m.picker;
22740         
22741         // hide month picker get's called when we called by 'before hide';
22742         
22743         var ignorehide = true;
22744         p.hideMonthPicker  = function(disableAnim){
22745             if (ignorehide) {
22746                 return;
22747             }
22748              if(this.monthPicker){
22749                 Roo.log("hideMonthPicker called");
22750                 if(disableAnim === true){
22751                     this.monthPicker.hide();
22752                 }else{
22753                     this.monthPicker.slideOut('t', {duration:.2});
22754                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22755                     p.fireEvent("select", this, this.value);
22756                     m.hide();
22757                 }
22758             }
22759         }
22760         
22761         Roo.log('picker set value');
22762         Roo.log(this.getValue());
22763         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22764         m.show(this.el, 'tl-bl?');
22765         ignorehide  = false;
22766         // this will trigger hideMonthPicker..
22767         
22768         
22769         // hidden the day picker
22770         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22771         
22772         
22773         
22774       
22775         
22776         p.showMonthPicker.defer(100, p);
22777     
22778         
22779        
22780     },
22781
22782     beforeBlur : function(){
22783         var v = this.parseDate(this.getRawValue());
22784         if(v){
22785             this.setValue(v);
22786         }
22787     }
22788
22789     /** @cfg {Boolean} grow @hide */
22790     /** @cfg {Number} growMin @hide */
22791     /** @cfg {Number} growMax @hide */
22792     /**
22793      * @hide
22794      * @method autoSize
22795      */
22796 });/*
22797  * Based on:
22798  * Ext JS Library 1.1.1
22799  * Copyright(c) 2006-2007, Ext JS, LLC.
22800  *
22801  * Originally Released Under LGPL - original licence link has changed is not relivant.
22802  *
22803  * Fork - LGPL
22804  * <script type="text/javascript">
22805  */
22806  
22807
22808 /**
22809  * @class Roo.form.ComboBox
22810  * @extends Roo.form.TriggerField
22811  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22812  * @constructor
22813  * Create a new ComboBox.
22814  * @param {Object} config Configuration options
22815  */
22816 Roo.form.ComboBox = function(config){
22817     Roo.form.ComboBox.superclass.constructor.call(this, config);
22818     this.addEvents({
22819         /**
22820          * @event expand
22821          * Fires when the dropdown list is expanded
22822              * @param {Roo.form.ComboBox} combo This combo box
22823              */
22824         'expand' : true,
22825         /**
22826          * @event collapse
22827          * Fires when the dropdown list is collapsed
22828              * @param {Roo.form.ComboBox} combo This combo box
22829              */
22830         'collapse' : true,
22831         /**
22832          * @event beforeselect
22833          * Fires before a list item is selected. Return false to cancel the selection.
22834              * @param {Roo.form.ComboBox} combo This combo box
22835              * @param {Roo.data.Record} record The data record returned from the underlying store
22836              * @param {Number} index The index of the selected item in the dropdown list
22837              */
22838         'beforeselect' : true,
22839         /**
22840          * @event select
22841          * Fires when a list item is selected
22842              * @param {Roo.form.ComboBox} combo This combo box
22843              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22844              * @param {Number} index The index of the selected item in the dropdown list
22845              */
22846         'select' : true,
22847         /**
22848          * @event beforequery
22849          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22850          * The event object passed has these properties:
22851              * @param {Roo.form.ComboBox} combo This combo box
22852              * @param {String} query The query
22853              * @param {Boolean} forceAll true to force "all" query
22854              * @param {Boolean} cancel true to cancel the query
22855              * @param {Object} e The query event object
22856              */
22857         'beforequery': true,
22858          /**
22859          * @event add
22860          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22861              * @param {Roo.form.ComboBox} combo This combo box
22862              */
22863         'add' : true,
22864         /**
22865          * @event edit
22866          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22867              * @param {Roo.form.ComboBox} combo This combo box
22868              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22869              */
22870         'edit' : true
22871         
22872         
22873     });
22874     if(this.transform){
22875         this.allowDomMove = false;
22876         var s = Roo.getDom(this.transform);
22877         if(!this.hiddenName){
22878             this.hiddenName = s.name;
22879         }
22880         if(!this.store){
22881             this.mode = 'local';
22882             var d = [], opts = s.options;
22883             for(var i = 0, len = opts.length;i < len; i++){
22884                 var o = opts[i];
22885                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22886                 if(o.selected) {
22887                     this.value = value;
22888                 }
22889                 d.push([value, o.text]);
22890             }
22891             this.store = new Roo.data.SimpleStore({
22892                 'id': 0,
22893                 fields: ['value', 'text'],
22894                 data : d
22895             });
22896             this.valueField = 'value';
22897             this.displayField = 'text';
22898         }
22899         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22900         if(!this.lazyRender){
22901             this.target = true;
22902             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22903             s.parentNode.removeChild(s); // remove it
22904             this.render(this.el.parentNode);
22905         }else{
22906             s.parentNode.removeChild(s); // remove it
22907         }
22908
22909     }
22910     if (this.store) {
22911         this.store = Roo.factory(this.store, Roo.data);
22912     }
22913     
22914     this.selectedIndex = -1;
22915     if(this.mode == 'local'){
22916         if(config.queryDelay === undefined){
22917             this.queryDelay = 10;
22918         }
22919         if(config.minChars === undefined){
22920             this.minChars = 0;
22921         }
22922     }
22923 };
22924
22925 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22926     /**
22927      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22928      */
22929     /**
22930      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22931      * rendering into an Roo.Editor, defaults to false)
22932      */
22933     /**
22934      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22935      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22936      */
22937     /**
22938      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22939      */
22940     /**
22941      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22942      * the dropdown list (defaults to undefined, with no header element)
22943      */
22944
22945      /**
22946      * @cfg {String/Roo.Template} tpl The template to use to render the output
22947      */
22948      
22949     // private
22950     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22951     /**
22952      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22953      */
22954     listWidth: undefined,
22955     /**
22956      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22957      * mode = 'remote' or 'text' if mode = 'local')
22958      */
22959     displayField: undefined,
22960     /**
22961      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22962      * mode = 'remote' or 'value' if mode = 'local'). 
22963      * Note: use of a valueField requires the user make a selection
22964      * in order for a value to be mapped.
22965      */
22966     valueField: undefined,
22967     
22968     
22969     /**
22970      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22971      * field's data value (defaults to the underlying DOM element's name)
22972      */
22973     hiddenName: undefined,
22974     /**
22975      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22976      */
22977     listClass: '',
22978     /**
22979      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22980      */
22981     selectedClass: 'x-combo-selected',
22982     /**
22983      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22984      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22985      * which displays a downward arrow icon).
22986      */
22987     triggerClass : 'x-form-arrow-trigger',
22988     /**
22989      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22990      */
22991     shadow:'sides',
22992     /**
22993      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22994      * anchor positions (defaults to 'tl-bl')
22995      */
22996     listAlign: 'tl-bl?',
22997     /**
22998      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22999      */
23000     maxHeight: 300,
23001     /**
23002      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23003      * query specified by the allQuery config option (defaults to 'query')
23004      */
23005     triggerAction: 'query',
23006     /**
23007      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23008      * (defaults to 4, does not apply if editable = false)
23009      */
23010     minChars : 4,
23011     /**
23012      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23013      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23014      */
23015     typeAhead: false,
23016     /**
23017      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23018      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23019      */
23020     queryDelay: 500,
23021     /**
23022      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23023      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23024      */
23025     pageSize: 0,
23026     /**
23027      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23028      * when editable = true (defaults to false)
23029      */
23030     selectOnFocus:false,
23031     /**
23032      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23033      */
23034     queryParam: 'query',
23035     /**
23036      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23037      * when mode = 'remote' (defaults to 'Loading...')
23038      */
23039     loadingText: 'Loading...',
23040     /**
23041      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23042      */
23043     resizable: false,
23044     /**
23045      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23046      */
23047     handleHeight : 8,
23048     /**
23049      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23050      * traditional select (defaults to true)
23051      */
23052     editable: true,
23053     /**
23054      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23055      */
23056     allQuery: '',
23057     /**
23058      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23059      */
23060     mode: 'remote',
23061     /**
23062      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23063      * listWidth has a higher value)
23064      */
23065     minListWidth : 70,
23066     /**
23067      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23068      * allow the user to set arbitrary text into the field (defaults to false)
23069      */
23070     forceSelection:false,
23071     /**
23072      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23073      * if typeAhead = true (defaults to 250)
23074      */
23075     typeAheadDelay : 250,
23076     /**
23077      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23078      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23079      */
23080     valueNotFoundText : undefined,
23081     /**
23082      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23083      */
23084     blockFocus : false,
23085     
23086     /**
23087      * @cfg {Boolean} disableClear Disable showing of clear button.
23088      */
23089     disableClear : false,
23090     /**
23091      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23092      */
23093     alwaysQuery : false,
23094     
23095     //private
23096     addicon : false,
23097     editicon: false,
23098     
23099     // element that contains real text value.. (when hidden is used..)
23100      
23101     // private
23102     onRender : function(ct, position){
23103         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23104         if(this.hiddenName){
23105             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23106                     'before', true);
23107             this.hiddenField.value =
23108                 this.hiddenValue !== undefined ? this.hiddenValue :
23109                 this.value !== undefined ? this.value : '';
23110
23111             // prevent input submission
23112             this.el.dom.removeAttribute('name');
23113              
23114              
23115         }
23116         if(Roo.isGecko){
23117             this.el.dom.setAttribute('autocomplete', 'off');
23118         }
23119
23120         var cls = 'x-combo-list';
23121
23122         this.list = new Roo.Layer({
23123             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23124         });
23125
23126         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23127         this.list.setWidth(lw);
23128         this.list.swallowEvent('mousewheel');
23129         this.assetHeight = 0;
23130
23131         if(this.title){
23132             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23133             this.assetHeight += this.header.getHeight();
23134         }
23135
23136         this.innerList = this.list.createChild({cls:cls+'-inner'});
23137         this.innerList.on('mouseover', this.onViewOver, this);
23138         this.innerList.on('mousemove', this.onViewMove, this);
23139         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23140         
23141         if(this.allowBlank && !this.pageSize && !this.disableClear){
23142             this.footer = this.list.createChild({cls:cls+'-ft'});
23143             this.pageTb = new Roo.Toolbar(this.footer);
23144            
23145         }
23146         if(this.pageSize){
23147             this.footer = this.list.createChild({cls:cls+'-ft'});
23148             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23149                     {pageSize: this.pageSize});
23150             
23151         }
23152         
23153         if (this.pageTb && this.allowBlank && !this.disableClear) {
23154             var _this = this;
23155             this.pageTb.add(new Roo.Toolbar.Fill(), {
23156                 cls: 'x-btn-icon x-btn-clear',
23157                 text: '&#160;',
23158                 handler: function()
23159                 {
23160                     _this.collapse();
23161                     _this.clearValue();
23162                     _this.onSelect(false, -1);
23163                 }
23164             });
23165         }
23166         if (this.footer) {
23167             this.assetHeight += this.footer.getHeight();
23168         }
23169         
23170
23171         if(!this.tpl){
23172             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23173         }
23174
23175         this.view = new Roo.View(this.innerList, this.tpl, {
23176             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23177         });
23178
23179         this.view.on('click', this.onViewClick, this);
23180
23181         this.store.on('beforeload', this.onBeforeLoad, this);
23182         this.store.on('load', this.onLoad, this);
23183         this.store.on('loadexception', this.onLoadException, this);
23184
23185         if(this.resizable){
23186             this.resizer = new Roo.Resizable(this.list,  {
23187                pinned:true, handles:'se'
23188             });
23189             this.resizer.on('resize', function(r, w, h){
23190                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23191                 this.listWidth = w;
23192                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23193                 this.restrictHeight();
23194             }, this);
23195             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23196         }
23197         if(!this.editable){
23198             this.editable = true;
23199             this.setEditable(false);
23200         }  
23201         
23202         
23203         if (typeof(this.events.add.listeners) != 'undefined') {
23204             
23205             this.addicon = this.wrap.createChild(
23206                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23207        
23208             this.addicon.on('click', function(e) {
23209                 this.fireEvent('add', this);
23210             }, this);
23211         }
23212         if (typeof(this.events.edit.listeners) != 'undefined') {
23213             
23214             this.editicon = this.wrap.createChild(
23215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23216             if (this.addicon) {
23217                 this.editicon.setStyle('margin-left', '40px');
23218             }
23219             this.editicon.on('click', function(e) {
23220                 
23221                 // we fire even  if inothing is selected..
23222                 this.fireEvent('edit', this, this.lastData );
23223                 
23224             }, this);
23225         }
23226         
23227         
23228         
23229     },
23230
23231     // private
23232     initEvents : function(){
23233         Roo.form.ComboBox.superclass.initEvents.call(this);
23234
23235         this.keyNav = new Roo.KeyNav(this.el, {
23236             "up" : function(e){
23237                 this.inKeyMode = true;
23238                 this.selectPrev();
23239             },
23240
23241             "down" : function(e){
23242                 if(!this.isExpanded()){
23243                     this.onTriggerClick();
23244                 }else{
23245                     this.inKeyMode = true;
23246                     this.selectNext();
23247                 }
23248             },
23249
23250             "enter" : function(e){
23251                 this.onViewClick();
23252                 //return true;
23253             },
23254
23255             "esc" : function(e){
23256                 this.collapse();
23257             },
23258
23259             "tab" : function(e){
23260                 this.onViewClick(false);
23261                 this.fireEvent("specialkey", this, e);
23262                 return true;
23263             },
23264
23265             scope : this,
23266
23267             doRelay : function(foo, bar, hname){
23268                 if(hname == 'down' || this.scope.isExpanded()){
23269                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23270                 }
23271                 return true;
23272             },
23273
23274             forceKeyDown: true
23275         });
23276         this.queryDelay = Math.max(this.queryDelay || 10,
23277                 this.mode == 'local' ? 10 : 250);
23278         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23279         if(this.typeAhead){
23280             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23281         }
23282         if(this.editable !== false){
23283             this.el.on("keyup", this.onKeyUp, this);
23284         }
23285         if(this.forceSelection){
23286             this.on('blur', this.doForce, this);
23287         }
23288     },
23289
23290     onDestroy : function(){
23291         if(this.view){
23292             this.view.setStore(null);
23293             this.view.el.removeAllListeners();
23294             this.view.el.remove();
23295             this.view.purgeListeners();
23296         }
23297         if(this.list){
23298             this.list.destroy();
23299         }
23300         if(this.store){
23301             this.store.un('beforeload', this.onBeforeLoad, this);
23302             this.store.un('load', this.onLoad, this);
23303             this.store.un('loadexception', this.onLoadException, this);
23304         }
23305         Roo.form.ComboBox.superclass.onDestroy.call(this);
23306     },
23307
23308     // private
23309     fireKey : function(e){
23310         if(e.isNavKeyPress() && !this.list.isVisible()){
23311             this.fireEvent("specialkey", this, e);
23312         }
23313     },
23314
23315     // private
23316     onResize: function(w, h){
23317         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23318         
23319         if(typeof w != 'number'){
23320             // we do not handle it!?!?
23321             return;
23322         }
23323         var tw = this.trigger.getWidth();
23324         tw += this.addicon ? this.addicon.getWidth() : 0;
23325         tw += this.editicon ? this.editicon.getWidth() : 0;
23326         var x = w - tw;
23327         this.el.setWidth( this.adjustWidth('input', x));
23328             
23329         this.trigger.setStyle('left', x+'px');
23330         
23331         if(this.list && this.listWidth === undefined){
23332             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23333             this.list.setWidth(lw);
23334             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23335         }
23336         
23337     
23338         
23339     },
23340
23341     /**
23342      * Allow or prevent the user from directly editing the field text.  If false is passed,
23343      * the user will only be able to select from the items defined in the dropdown list.  This method
23344      * is the runtime equivalent of setting the 'editable' config option at config time.
23345      * @param {Boolean} value True to allow the user to directly edit the field text
23346      */
23347     setEditable : function(value){
23348         if(value == this.editable){
23349             return;
23350         }
23351         this.editable = value;
23352         if(!value){
23353             this.el.dom.setAttribute('readOnly', true);
23354             this.el.on('mousedown', this.onTriggerClick,  this);
23355             this.el.addClass('x-combo-noedit');
23356         }else{
23357             this.el.dom.setAttribute('readOnly', false);
23358             this.el.un('mousedown', this.onTriggerClick,  this);
23359             this.el.removeClass('x-combo-noedit');
23360         }
23361     },
23362
23363     // private
23364     onBeforeLoad : function(){
23365         if(!this.hasFocus){
23366             return;
23367         }
23368         this.innerList.update(this.loadingText ?
23369                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23370         this.restrictHeight();
23371         this.selectedIndex = -1;
23372     },
23373
23374     // private
23375     onLoad : function(){
23376         if(!this.hasFocus){
23377             return;
23378         }
23379         if(this.store.getCount() > 0){
23380             this.expand();
23381             this.restrictHeight();
23382             if(this.lastQuery == this.allQuery){
23383                 if(this.editable){
23384                     this.el.dom.select();
23385                 }
23386                 if(!this.selectByValue(this.value, true)){
23387                     this.select(0, true);
23388                 }
23389             }else{
23390                 this.selectNext();
23391                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23392                     this.taTask.delay(this.typeAheadDelay);
23393                 }
23394             }
23395         }else{
23396             this.onEmptyResults();
23397         }
23398         //this.el.focus();
23399     },
23400     // private
23401     onLoadException : function()
23402     {
23403         this.collapse();
23404         Roo.log(this.store.reader.jsonData);
23405         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23406             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23407         }
23408         
23409         
23410     },
23411     // private
23412     onTypeAhead : function(){
23413         if(this.store.getCount() > 0){
23414             var r = this.store.getAt(0);
23415             var newValue = r.data[this.displayField];
23416             var len = newValue.length;
23417             var selStart = this.getRawValue().length;
23418             if(selStart != len){
23419                 this.setRawValue(newValue);
23420                 this.selectText(selStart, newValue.length);
23421             }
23422         }
23423     },
23424
23425     // private
23426     onSelect : function(record, index){
23427         if(this.fireEvent('beforeselect', this, record, index) !== false){
23428             this.setFromData(index > -1 ? record.data : false);
23429             this.collapse();
23430             this.fireEvent('select', this, record, index);
23431         }
23432     },
23433
23434     /**
23435      * Returns the currently selected field value or empty string if no value is set.
23436      * @return {String} value The selected value
23437      */
23438     getValue : function(){
23439         if(this.valueField){
23440             return typeof this.value != 'undefined' ? this.value : '';
23441         }
23442         return Roo.form.ComboBox.superclass.getValue.call(this);
23443     },
23444
23445     /**
23446      * Clears any text/value currently set in the field
23447      */
23448     clearValue : function(){
23449         if(this.hiddenField){
23450             this.hiddenField.value = '';
23451         }
23452         this.value = '';
23453         this.setRawValue('');
23454         this.lastSelectionText = '';
23455         
23456     },
23457
23458     /**
23459      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23460      * will be displayed in the field.  If the value does not match the data value of an existing item,
23461      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23462      * Otherwise the field will be blank (although the value will still be set).
23463      * @param {String} value The value to match
23464      */
23465     setValue : function(v){
23466         var text = v;
23467         if(this.valueField){
23468             var r = this.findRecord(this.valueField, v);
23469             if(r){
23470                 text = r.data[this.displayField];
23471             }else if(this.valueNotFoundText !== undefined){
23472                 text = this.valueNotFoundText;
23473             }
23474         }
23475         this.lastSelectionText = text;
23476         if(this.hiddenField){
23477             this.hiddenField.value = v;
23478         }
23479         Roo.form.ComboBox.superclass.setValue.call(this, text);
23480         this.value = v;
23481     },
23482     /**
23483      * @property {Object} the last set data for the element
23484      */
23485     
23486     lastData : false,
23487     /**
23488      * Sets the value of the field based on a object which is related to the record format for the store.
23489      * @param {Object} value the value to set as. or false on reset?
23490      */
23491     setFromData : function(o){
23492         var dv = ''; // display value
23493         var vv = ''; // value value..
23494         this.lastData = o;
23495         if (this.displayField) {
23496             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23497         } else {
23498             // this is an error condition!!!
23499             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23500         }
23501         
23502         if(this.valueField){
23503             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23504         }
23505         if(this.hiddenField){
23506             this.hiddenField.value = vv;
23507             
23508             this.lastSelectionText = dv;
23509             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23510             this.value = vv;
23511             return;
23512         }
23513         // no hidden field.. - we store the value in 'value', but still display
23514         // display field!!!!
23515         this.lastSelectionText = dv;
23516         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23517         this.value = vv;
23518         
23519         
23520     },
23521     // private
23522     reset : function(){
23523         // overridden so that last data is reset..
23524         this.setValue(this.resetValue);
23525         this.clearInvalid();
23526         this.lastData = false;
23527         if (this.view) {
23528             this.view.clearSelections();
23529         }
23530     },
23531     // private
23532     findRecord : function(prop, value){
23533         var record;
23534         if(this.store.getCount() > 0){
23535             this.store.each(function(r){
23536                 if(r.data[prop] == value){
23537                     record = r;
23538                     return false;
23539                 }
23540                 return true;
23541             });
23542         }
23543         return record;
23544     },
23545     
23546     getName: function()
23547     {
23548         // returns hidden if it's set..
23549         if (!this.rendered) {return ''};
23550         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23551         
23552     },
23553     // private
23554     onViewMove : function(e, t){
23555         this.inKeyMode = false;
23556     },
23557
23558     // private
23559     onViewOver : function(e, t){
23560         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23561             return;
23562         }
23563         var item = this.view.findItemFromChild(t);
23564         if(item){
23565             var index = this.view.indexOf(item);
23566             this.select(index, false);
23567         }
23568     },
23569
23570     // private
23571     onViewClick : function(doFocus)
23572     {
23573         var index = this.view.getSelectedIndexes()[0];
23574         var r = this.store.getAt(index);
23575         if(r){
23576             this.onSelect(r, index);
23577         }
23578         if(doFocus !== false && !this.blockFocus){
23579             this.el.focus();
23580         }
23581     },
23582
23583     // private
23584     restrictHeight : function(){
23585         this.innerList.dom.style.height = '';
23586         var inner = this.innerList.dom;
23587         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23588         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23589         this.list.beginUpdate();
23590         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23591         this.list.alignTo(this.el, this.listAlign);
23592         this.list.endUpdate();
23593     },
23594
23595     // private
23596     onEmptyResults : function(){
23597         this.collapse();
23598     },
23599
23600     /**
23601      * Returns true if the dropdown list is expanded, else false.
23602      */
23603     isExpanded : function(){
23604         return this.list.isVisible();
23605     },
23606
23607     /**
23608      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23609      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23610      * @param {String} value The data value of the item to select
23611      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23612      * selected item if it is not currently in view (defaults to true)
23613      * @return {Boolean} True if the value matched an item in the list, else false
23614      */
23615     selectByValue : function(v, scrollIntoView){
23616         if(v !== undefined && v !== null){
23617             var r = this.findRecord(this.valueField || this.displayField, v);
23618             if(r){
23619                 this.select(this.store.indexOf(r), scrollIntoView);
23620                 return true;
23621             }
23622         }
23623         return false;
23624     },
23625
23626     /**
23627      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23628      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23629      * @param {Number} index The zero-based index of the list item to select
23630      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23631      * selected item if it is not currently in view (defaults to true)
23632      */
23633     select : function(index, scrollIntoView){
23634         this.selectedIndex = index;
23635         this.view.select(index);
23636         if(scrollIntoView !== false){
23637             var el = this.view.getNode(index);
23638             if(el){
23639                 this.innerList.scrollChildIntoView(el, false);
23640             }
23641         }
23642     },
23643
23644     // private
23645     selectNext : function(){
23646         var ct = this.store.getCount();
23647         if(ct > 0){
23648             if(this.selectedIndex == -1){
23649                 this.select(0);
23650             }else if(this.selectedIndex < ct-1){
23651                 this.select(this.selectedIndex+1);
23652             }
23653         }
23654     },
23655
23656     // private
23657     selectPrev : function(){
23658         var ct = this.store.getCount();
23659         if(ct > 0){
23660             if(this.selectedIndex == -1){
23661                 this.select(0);
23662             }else if(this.selectedIndex != 0){
23663                 this.select(this.selectedIndex-1);
23664             }
23665         }
23666     },
23667
23668     // private
23669     onKeyUp : function(e){
23670         if(this.editable !== false && !e.isSpecialKey()){
23671             this.lastKey = e.getKey();
23672             this.dqTask.delay(this.queryDelay);
23673         }
23674     },
23675
23676     // private
23677     validateBlur : function(){
23678         return !this.list || !this.list.isVisible();   
23679     },
23680
23681     // private
23682     initQuery : function(){
23683         this.doQuery(this.getRawValue());
23684     },
23685
23686     // private
23687     doForce : function(){
23688         if(this.el.dom.value.length > 0){
23689             this.el.dom.value =
23690                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23691              
23692         }
23693     },
23694
23695     /**
23696      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23697      * query allowing the query action to be canceled if needed.
23698      * @param {String} query The SQL query to execute
23699      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23700      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23701      * saved in the current store (defaults to false)
23702      */
23703     doQuery : function(q, forceAll){
23704         if(q === undefined || q === null){
23705             q = '';
23706         }
23707         var qe = {
23708             query: q,
23709             forceAll: forceAll,
23710             combo: this,
23711             cancel:false
23712         };
23713         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23714             return false;
23715         }
23716         q = qe.query;
23717         forceAll = qe.forceAll;
23718         if(forceAll === true || (q.length >= this.minChars)){
23719             if(this.lastQuery != q || this.alwaysQuery){
23720                 this.lastQuery = q;
23721                 if(this.mode == 'local'){
23722                     this.selectedIndex = -1;
23723                     if(forceAll){
23724                         this.store.clearFilter();
23725                     }else{
23726                         this.store.filter(this.displayField, q);
23727                     }
23728                     this.onLoad();
23729                 }else{
23730                     this.store.baseParams[this.queryParam] = q;
23731                     this.store.load({
23732                         params: this.getParams(q)
23733                     });
23734                     this.expand();
23735                 }
23736             }else{
23737                 this.selectedIndex = -1;
23738                 this.onLoad();   
23739             }
23740         }
23741     },
23742
23743     // private
23744     getParams : function(q){
23745         var p = {};
23746         //p[this.queryParam] = q;
23747         if(this.pageSize){
23748             p.start = 0;
23749             p.limit = this.pageSize;
23750         }
23751         return p;
23752     },
23753
23754     /**
23755      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23756      */
23757     collapse : function(){
23758         if(!this.isExpanded()){
23759             return;
23760         }
23761         this.list.hide();
23762         Roo.get(document).un('mousedown', this.collapseIf, this);
23763         Roo.get(document).un('mousewheel', this.collapseIf, this);
23764         if (!this.editable) {
23765             Roo.get(document).un('keydown', this.listKeyPress, this);
23766         }
23767         this.fireEvent('collapse', this);
23768     },
23769
23770     // private
23771     collapseIf : function(e){
23772         if(!e.within(this.wrap) && !e.within(this.list)){
23773             this.collapse();
23774         }
23775     },
23776
23777     /**
23778      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23779      */
23780     expand : function(){
23781         if(this.isExpanded() || !this.hasFocus){
23782             return;
23783         }
23784         this.list.alignTo(this.el, this.listAlign);
23785         this.list.show();
23786         Roo.get(document).on('mousedown', this.collapseIf, this);
23787         Roo.get(document).on('mousewheel', this.collapseIf, this);
23788         if (!this.editable) {
23789             Roo.get(document).on('keydown', this.listKeyPress, this);
23790         }
23791         
23792         this.fireEvent('expand', this);
23793     },
23794
23795     // private
23796     // Implements the default empty TriggerField.onTriggerClick function
23797     onTriggerClick : function(){
23798         if(this.disabled){
23799             return;
23800         }
23801         if(this.isExpanded()){
23802             this.collapse();
23803             if (!this.blockFocus) {
23804                 this.el.focus();
23805             }
23806             
23807         }else {
23808             this.hasFocus = true;
23809             if(this.triggerAction == 'all') {
23810                 this.doQuery(this.allQuery, true);
23811             } else {
23812                 this.doQuery(this.getRawValue());
23813             }
23814             if (!this.blockFocus) {
23815                 this.el.focus();
23816             }
23817         }
23818     },
23819     listKeyPress : function(e)
23820     {
23821         //Roo.log('listkeypress');
23822         // scroll to first matching element based on key pres..
23823         if (e.isSpecialKey()) {
23824             return false;
23825         }
23826         var k = String.fromCharCode(e.getKey()).toUpperCase();
23827         //Roo.log(k);
23828         var match  = false;
23829         var csel = this.view.getSelectedNodes();
23830         var cselitem = false;
23831         if (csel.length) {
23832             var ix = this.view.indexOf(csel[0]);
23833             cselitem  = this.store.getAt(ix);
23834             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23835                 cselitem = false;
23836             }
23837             
23838         }
23839         
23840         this.store.each(function(v) { 
23841             if (cselitem) {
23842                 // start at existing selection.
23843                 if (cselitem.id == v.id) {
23844                     cselitem = false;
23845                 }
23846                 return;
23847             }
23848                 
23849             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23850                 match = this.store.indexOf(v);
23851                 return false;
23852             }
23853         }, this);
23854         
23855         if (match === false) {
23856             return true; // no more action?
23857         }
23858         // scroll to?
23859         this.view.select(match);
23860         var sn = Roo.get(this.view.getSelectedNodes()[0])
23861         sn.scrollIntoView(sn.dom.parentNode, false);
23862     }
23863
23864     /** 
23865     * @cfg {Boolean} grow 
23866     * @hide 
23867     */
23868     /** 
23869     * @cfg {Number} growMin 
23870     * @hide 
23871     */
23872     /** 
23873     * @cfg {Number} growMax 
23874     * @hide 
23875     */
23876     /**
23877      * @hide
23878      * @method autoSize
23879      */
23880 });/*
23881  * Copyright(c) 2010-2012, Roo J Solutions Limited
23882  *
23883  * Licence LGPL
23884  *
23885  */
23886
23887 /**
23888  * @class Roo.form.ComboBoxArray
23889  * @extends Roo.form.TextField
23890  * A facebook style adder... for lists of email / people / countries  etc...
23891  * pick multiple items from a combo box, and shows each one.
23892  *
23893  *  Fred [x]  Brian [x]  [Pick another |v]
23894  *
23895  *
23896  *  For this to work: it needs various extra information
23897  *    - normal combo problay has
23898  *      name, hiddenName
23899  *    + displayField, valueField
23900  *
23901  *    For our purpose...
23902  *
23903  *
23904  *   If we change from 'extends' to wrapping...
23905  *   
23906  *  
23907  *
23908  
23909  
23910  * @constructor
23911  * Create a new ComboBoxArray.
23912  * @param {Object} config Configuration options
23913  */
23914  
23915
23916 Roo.form.ComboBoxArray = function(config)
23917 {
23918     this.addEvents({
23919         /**
23920          * @event beforeremove
23921          * Fires before remove the value from the list
23922              * @param {Roo.form.ComboBoxArray} _self This combo box array
23923              * @param {Roo.form.ComboBoxArray.Item} item removed item
23924              */
23925         'beforeremove' : true,
23926         /**
23927          * @event remove
23928          * Fires when remove the value from the list
23929              * @param {Roo.form.ComboBoxArray} _self This combo box array
23930              * @param {Roo.form.ComboBoxArray.Item} item removed item
23931              */
23932         'remove' : true
23933         
23934         
23935     });
23936     
23937     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23938     
23939     this.items = new Roo.util.MixedCollection(false);
23940     
23941     // construct the child combo...
23942     
23943     
23944     
23945     
23946    
23947     
23948 }
23949
23950  
23951 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23952
23953     /**
23954      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23955      */
23956     
23957     lastData : false,
23958     
23959     // behavies liek a hiddne field
23960     inputType:      'hidden',
23961     /**
23962      * @cfg {Number} width The width of the box that displays the selected element
23963      */ 
23964     width:          300,
23965
23966     
23967     
23968     /**
23969      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23970      */
23971     name : false,
23972     /**
23973      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23974      */
23975     hiddenName : false,
23976     
23977     
23978     // private the array of items that are displayed..
23979     items  : false,
23980     // private - the hidden field el.
23981     hiddenEl : false,
23982     // private - the filed el..
23983     el : false,
23984     
23985     //validateValue : function() { return true; }, // all values are ok!
23986     //onAddClick: function() { },
23987     
23988     onRender : function(ct, position) 
23989     {
23990         
23991         // create the standard hidden element
23992         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23993         
23994         
23995         // give fake names to child combo;
23996         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23997         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23998         
23999         this.combo = Roo.factory(this.combo, Roo.form);
24000         this.combo.onRender(ct, position);
24001         if (typeof(this.combo.width) != 'undefined') {
24002             this.combo.onResize(this.combo.width,0);
24003         }
24004         
24005         this.combo.initEvents();
24006         
24007         // assigned so form know we need to do this..
24008         this.store          = this.combo.store;
24009         this.valueField     = this.combo.valueField;
24010         this.displayField   = this.combo.displayField ;
24011         
24012         
24013         this.combo.wrap.addClass('x-cbarray-grp');
24014         
24015         var cbwrap = this.combo.wrap.createChild(
24016             {tag: 'div', cls: 'x-cbarray-cb'},
24017             this.combo.el.dom
24018         );
24019         
24020              
24021         this.hiddenEl = this.combo.wrap.createChild({
24022             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24023         });
24024         this.el = this.combo.wrap.createChild({
24025             tag: 'input',  type:'hidden' , name: this.name, value : ''
24026         });
24027          //   this.el.dom.removeAttribute("name");
24028         
24029         
24030         this.outerWrap = this.combo.wrap;
24031         this.wrap = cbwrap;
24032         
24033         this.outerWrap.setWidth(this.width);
24034         this.outerWrap.dom.removeChild(this.el.dom);
24035         
24036         this.wrap.dom.appendChild(this.el.dom);
24037         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24038         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24039         
24040         this.combo.trigger.setStyle('position','relative');
24041         this.combo.trigger.setStyle('left', '0px');
24042         this.combo.trigger.setStyle('top', '2px');
24043         
24044         this.combo.el.setStyle('vertical-align', 'text-bottom');
24045         
24046         //this.trigger.setStyle('vertical-align', 'top');
24047         
24048         // this should use the code from combo really... on('add' ....)
24049         if (this.adder) {
24050             
24051         
24052             this.adder = this.outerWrap.createChild(
24053                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24054             var _t = this;
24055             this.adder.on('click', function(e) {
24056                 _t.fireEvent('adderclick', this, e);
24057             }, _t);
24058         }
24059         //var _t = this;
24060         //this.adder.on('click', this.onAddClick, _t);
24061         
24062         
24063         this.combo.on('select', function(cb, rec, ix) {
24064             this.addItem(rec.data);
24065             
24066             cb.setValue('');
24067             cb.el.dom.value = '';
24068             //cb.lastData = rec.data;
24069             // add to list
24070             
24071         }, this);
24072         
24073         
24074     },
24075     
24076     
24077     getName: function()
24078     {
24079         // returns hidden if it's set..
24080         if (!this.rendered) {return ''};
24081         return  this.hiddenName ? this.hiddenName : this.name;
24082         
24083     },
24084     
24085     
24086     onResize: function(w, h){
24087         
24088         return;
24089         // not sure if this is needed..
24090         //this.combo.onResize(w,h);
24091         
24092         if(typeof w != 'number'){
24093             // we do not handle it!?!?
24094             return;
24095         }
24096         var tw = this.combo.trigger.getWidth();
24097         tw += this.addicon ? this.addicon.getWidth() : 0;
24098         tw += this.editicon ? this.editicon.getWidth() : 0;
24099         var x = w - tw;
24100         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24101             
24102         this.combo.trigger.setStyle('left', '0px');
24103         
24104         if(this.list && this.listWidth === undefined){
24105             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24106             this.list.setWidth(lw);
24107             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24108         }
24109         
24110     
24111         
24112     },
24113     
24114     addItem: function(rec)
24115     {
24116         var valueField = this.combo.valueField;
24117         var displayField = this.combo.displayField;
24118         if (this.items.indexOfKey(rec[valueField]) > -1) {
24119             //console.log("GOT " + rec.data.id);
24120             return;
24121         }
24122         
24123         var x = new Roo.form.ComboBoxArray.Item({
24124             //id : rec[this.idField],
24125             data : rec,
24126             displayField : displayField ,
24127             tipField : displayField ,
24128             cb : this
24129         });
24130         // use the 
24131         this.items.add(rec[valueField],x);
24132         // add it before the element..
24133         this.updateHiddenEl();
24134         x.render(this.outerWrap, this.wrap.dom);
24135         // add the image handler..
24136     },
24137     
24138     updateHiddenEl : function()
24139     {
24140         this.validate();
24141         if (!this.hiddenEl) {
24142             return;
24143         }
24144         var ar = [];
24145         var idField = this.combo.valueField;
24146         
24147         this.items.each(function(f) {
24148             ar.push(f.data[idField]);
24149            
24150         });
24151         this.hiddenEl.dom.value = ar.join(',');
24152         this.validate();
24153     },
24154     
24155     reset : function()
24156     {
24157         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24158         this.items.each(function(f) {
24159            f.remove(); 
24160         });
24161         this.el.dom.value = '';
24162         if (this.hiddenEl) {
24163             this.hiddenEl.dom.value = '';
24164         }
24165         
24166     },
24167     getValue: function()
24168     {
24169         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24170     },
24171     setValue: function(v) // not a valid action - must use addItems..
24172     {
24173          
24174         this.reset();
24175         
24176         
24177         
24178         if (this.store.isLocal && (typeof(v) == 'string')) {
24179             // then we can use the store to find the values..
24180             // comma seperated at present.. this needs to allow JSON based encoding..
24181             this.hiddenEl.value  = v;
24182             var v_ar = [];
24183             Roo.each(v.split(','), function(k) {
24184                 Roo.log("CHECK " + this.valueField + ',' + k);
24185                 var li = this.store.query(this.valueField, k);
24186                 if (!li.length) {
24187                     return;
24188                 }
24189                 var add = {};
24190                 add[this.valueField] = k;
24191                 add[this.displayField] = li.item(0).data[this.displayField];
24192                 
24193                 this.addItem(add);
24194             }, this) 
24195              
24196         }
24197         if (typeof(v) == 'object' ) {
24198             // then let's assume it's an array of objects..
24199             Roo.each(v, function(l) {
24200                 this.addItem(l);
24201             }, this);
24202              
24203         }
24204         
24205         
24206     },
24207     setFromData: function(v)
24208     {
24209         // this recieves an object, if setValues is called.
24210         this.reset();
24211         this.el.dom.value = v[this.displayField];
24212         this.hiddenEl.dom.value = v[this.valueField];
24213         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24214             return;
24215         }
24216         var kv = v[this.valueField];
24217         var dv = v[this.displayField];
24218         kv = typeof(kv) != 'string' ? '' : kv;
24219         dv = typeof(dv) != 'string' ? '' : dv;
24220         
24221         
24222         var keys = kv.split(',');
24223         var display = dv.split(',');
24224         for (var i = 0 ; i < keys.length; i++) {
24225             
24226             add = {};
24227             add[this.valueField] = keys[i];
24228             add[this.displayField] = display[i];
24229             this.addItem(add);
24230         }
24231       
24232         
24233     },
24234     
24235     /**
24236      * Validates the combox array value
24237      * @return {Boolean} True if the value is valid, else false
24238      */
24239     validate : function(){
24240         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24241             this.clearInvalid();
24242             return true;
24243         }
24244         return false;
24245     },
24246     
24247     validateValue : function(value){
24248         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24249         
24250     },
24251     
24252     /*@
24253      * overide
24254      * 
24255      */
24256     isDirty : function() {
24257         if(this.disabled) {
24258             return false;
24259         }
24260         
24261         try {
24262             var d = Roo.decode(String(this.originalValue));
24263         } catch (e) {
24264             return String(this.getValue()) !== String(this.originalValue);
24265         }
24266         
24267         var originalValue = [];
24268         
24269         for (var i = 0; i < d.length; i++){
24270             originalValue.push(d[i][this.valueField]);
24271         }
24272         
24273         return String(this.getValue()) !== String(originalValue.join(','));
24274         
24275     }
24276     
24277 });
24278
24279
24280
24281 /**
24282  * @class Roo.form.ComboBoxArray.Item
24283  * @extends Roo.BoxComponent
24284  * A selected item in the list
24285  *  Fred [x]  Brian [x]  [Pick another |v]
24286  * 
24287  * @constructor
24288  * Create a new item.
24289  * @param {Object} config Configuration options
24290  */
24291  
24292 Roo.form.ComboBoxArray.Item = function(config) {
24293     config.id = Roo.id();
24294     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24295 }
24296
24297 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24298     data : {},
24299     cb: false,
24300     displayField : false,
24301     tipField : false,
24302     
24303     
24304     defaultAutoCreate : {
24305         tag: 'div',
24306         cls: 'x-cbarray-item',
24307         cn : [ 
24308             { tag: 'div' },
24309             {
24310                 tag: 'img',
24311                 width:16,
24312                 height : 16,
24313                 src : Roo.BLANK_IMAGE_URL ,
24314                 align: 'center'
24315             }
24316         ]
24317         
24318     },
24319     
24320  
24321     onRender : function(ct, position)
24322     {
24323         Roo.form.Field.superclass.onRender.call(this, ct, position);
24324         
24325         if(!this.el){
24326             var cfg = this.getAutoCreate();
24327             this.el = ct.createChild(cfg, position);
24328         }
24329         
24330         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24331         
24332         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24333             this.cb.renderer(this.data) :
24334             String.format('{0}',this.data[this.displayField]);
24335         
24336             
24337         this.el.child('div').dom.setAttribute('qtip',
24338                         String.format('{0}',this.data[this.tipField])
24339         );
24340         
24341         this.el.child('img').on('click', this.remove, this);
24342         
24343     },
24344    
24345     remove : function()
24346     {
24347         if(this.cb.disabled){
24348             return;
24349         }
24350         
24351         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24352             this.cb.items.remove(this);
24353             this.el.child('img').un('click', this.remove, this);
24354             this.el.remove();
24355             this.cb.updateHiddenEl();
24356
24357             this.cb.fireEvent('remove', this.cb, this);
24358         }
24359         
24360     }
24361 });/*
24362  * Based on:
24363  * Ext JS Library 1.1.1
24364  * Copyright(c) 2006-2007, Ext JS, LLC.
24365  *
24366  * Originally Released Under LGPL - original licence link has changed is not relivant.
24367  *
24368  * Fork - LGPL
24369  * <script type="text/javascript">
24370  */
24371 /**
24372  * @class Roo.form.Checkbox
24373  * @extends Roo.form.Field
24374  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24375  * @constructor
24376  * Creates a new Checkbox
24377  * @param {Object} config Configuration options
24378  */
24379 Roo.form.Checkbox = function(config){
24380     Roo.form.Checkbox.superclass.constructor.call(this, config);
24381     this.addEvents({
24382         /**
24383          * @event check
24384          * Fires when the checkbox is checked or unchecked.
24385              * @param {Roo.form.Checkbox} this This checkbox
24386              * @param {Boolean} checked The new checked value
24387              */
24388         check : true
24389     });
24390 };
24391
24392 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24393     /**
24394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24395      */
24396     focusClass : undefined,
24397     /**
24398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24399      */
24400     fieldClass: "x-form-field",
24401     /**
24402      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24403      */
24404     checked: false,
24405     /**
24406      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24407      * {tag: "input", type: "checkbox", autocomplete: "off"})
24408      */
24409     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24410     /**
24411      * @cfg {String} boxLabel The text that appears beside the checkbox
24412      */
24413     boxLabel : "",
24414     /**
24415      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24416      */  
24417     inputValue : '1',
24418     /**
24419      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24420      */
24421      valueOff: '0', // value when not checked..
24422
24423     actionMode : 'viewEl', 
24424     //
24425     // private
24426     itemCls : 'x-menu-check-item x-form-item',
24427     groupClass : 'x-menu-group-item',
24428     inputType : 'hidden',
24429     
24430     
24431     inSetChecked: false, // check that we are not calling self...
24432     
24433     inputElement: false, // real input element?
24434     basedOn: false, // ????
24435     
24436     isFormField: true, // not sure where this is needed!!!!
24437
24438     onResize : function(){
24439         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24440         if(!this.boxLabel){
24441             this.el.alignTo(this.wrap, 'c-c');
24442         }
24443     },
24444
24445     initEvents : function(){
24446         Roo.form.Checkbox.superclass.initEvents.call(this);
24447         this.el.on("click", this.onClick,  this);
24448         this.el.on("change", this.onClick,  this);
24449     },
24450
24451
24452     getResizeEl : function(){
24453         return this.wrap;
24454     },
24455
24456     getPositionEl : function(){
24457         return this.wrap;
24458     },
24459
24460     // private
24461     onRender : function(ct, position){
24462         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24463         /*
24464         if(this.inputValue !== undefined){
24465             this.el.dom.value = this.inputValue;
24466         }
24467         */
24468         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24469         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24470         var viewEl = this.wrap.createChild({ 
24471             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24472         this.viewEl = viewEl;   
24473         this.wrap.on('click', this.onClick,  this); 
24474         
24475         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24476         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24477         
24478         
24479         
24480         if(this.boxLabel){
24481             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24482         //    viewEl.on('click', this.onClick,  this); 
24483         }
24484         //if(this.checked){
24485             this.setChecked(this.checked);
24486         //}else{
24487             //this.checked = this.el.dom;
24488         //}
24489
24490     },
24491
24492     // private
24493     initValue : Roo.emptyFn,
24494
24495     /**
24496      * Returns the checked state of the checkbox.
24497      * @return {Boolean} True if checked, else false
24498      */
24499     getValue : function(){
24500         if(this.el){
24501             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24502         }
24503         return this.valueOff;
24504         
24505     },
24506
24507         // private
24508     onClick : function(){ 
24509         if (this.disabled) {
24510             return;
24511         }
24512         this.setChecked(!this.checked);
24513
24514         //if(this.el.dom.checked != this.checked){
24515         //    this.setValue(this.el.dom.checked);
24516        // }
24517     },
24518
24519     /**
24520      * Sets the checked state of the checkbox.
24521      * On is always based on a string comparison between inputValue and the param.
24522      * @param {Boolean/String} value - the value to set 
24523      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24524      */
24525     setValue : function(v,suppressEvent){
24526         
24527         
24528         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24529         //if(this.el && this.el.dom){
24530         //    this.el.dom.checked = this.checked;
24531         //    this.el.dom.defaultChecked = this.checked;
24532         //}
24533         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24534         //this.fireEvent("check", this, this.checked);
24535     },
24536     // private..
24537     setChecked : function(state,suppressEvent)
24538     {
24539         if (this.inSetChecked) {
24540             this.checked = state;
24541             return;
24542         }
24543         
24544     
24545         if(this.wrap){
24546             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24547         }
24548         this.checked = state;
24549         if(suppressEvent !== true){
24550             this.fireEvent('check', this, state);
24551         }
24552         this.inSetChecked = true;
24553         this.el.dom.value = state ? this.inputValue : this.valueOff;
24554         this.inSetChecked = false;
24555         
24556     },
24557     // handle setting of hidden value by some other method!!?!?
24558     setFromHidden: function()
24559     {
24560         if(!this.el){
24561             return;
24562         }
24563         //console.log("SET FROM HIDDEN");
24564         //alert('setFrom hidden');
24565         this.setValue(this.el.dom.value);
24566     },
24567     
24568     onDestroy : function()
24569     {
24570         if(this.viewEl){
24571             Roo.get(this.viewEl).remove();
24572         }
24573          
24574         Roo.form.Checkbox.superclass.onDestroy.call(this);
24575     }
24576
24577 });/*
24578  * Based on:
24579  * Ext JS Library 1.1.1
24580  * Copyright(c) 2006-2007, Ext JS, LLC.
24581  *
24582  * Originally Released Under LGPL - original licence link has changed is not relivant.
24583  *
24584  * Fork - LGPL
24585  * <script type="text/javascript">
24586  */
24587  
24588 /**
24589  * @class Roo.form.Radio
24590  * @extends Roo.form.Checkbox
24591  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24592  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24593  * @constructor
24594  * Creates a new Radio
24595  * @param {Object} config Configuration options
24596  */
24597 Roo.form.Radio = function(){
24598     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24599 };
24600 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24601     inputType: 'radio',
24602
24603     /**
24604      * If this radio is part of a group, it will return the selected value
24605      * @return {String}
24606      */
24607     getGroupValue : function(){
24608         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24609     },
24610     
24611     
24612     onRender : function(ct, position){
24613         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24614         
24615         if(this.inputValue !== undefined){
24616             this.el.dom.value = this.inputValue;
24617         }
24618          
24619         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24620         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24621         //var viewEl = this.wrap.createChild({ 
24622         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24623         //this.viewEl = viewEl;   
24624         //this.wrap.on('click', this.onClick,  this); 
24625         
24626         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24627         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24628         
24629         
24630         
24631         if(this.boxLabel){
24632             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24633         //    viewEl.on('click', this.onClick,  this); 
24634         }
24635          if(this.checked){
24636             this.el.dom.checked =   'checked' ;
24637         }
24638          
24639     } 
24640     
24641     
24642 });//<script type="text/javascript">
24643
24644 /*
24645  * Based  Ext JS Library 1.1.1
24646  * Copyright(c) 2006-2007, Ext JS, LLC.
24647  * LGPL
24648  *
24649  */
24650  
24651 /**
24652  * @class Roo.HtmlEditorCore
24653  * @extends Roo.Component
24654  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24655  *
24656  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24657  */
24658
24659 Roo.HtmlEditorCore = function(config){
24660     
24661     
24662     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24663     
24664     
24665     this.addEvents({
24666         /**
24667          * @event initialize
24668          * Fires when the editor is fully initialized (including the iframe)
24669          * @param {Roo.HtmlEditorCore} this
24670          */
24671         initialize: true,
24672         /**
24673          * @event activate
24674          * Fires when the editor is first receives the focus. Any insertion must wait
24675          * until after this event.
24676          * @param {Roo.HtmlEditorCore} this
24677          */
24678         activate: true,
24679          /**
24680          * @event beforesync
24681          * Fires before the textarea is updated with content from the editor iframe. Return false
24682          * to cancel the sync.
24683          * @param {Roo.HtmlEditorCore} this
24684          * @param {String} html
24685          */
24686         beforesync: true,
24687          /**
24688          * @event beforepush
24689          * Fires before the iframe editor is updated with content from the textarea. Return false
24690          * to cancel the push.
24691          * @param {Roo.HtmlEditorCore} this
24692          * @param {String} html
24693          */
24694         beforepush: true,
24695          /**
24696          * @event sync
24697          * Fires when the textarea is updated with content from the editor iframe.
24698          * @param {Roo.HtmlEditorCore} this
24699          * @param {String} html
24700          */
24701         sync: true,
24702          /**
24703          * @event push
24704          * Fires when the iframe editor is updated with content from the textarea.
24705          * @param {Roo.HtmlEditorCore} this
24706          * @param {String} html
24707          */
24708         push: true,
24709         
24710         /**
24711          * @event editorevent
24712          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24713          * @param {Roo.HtmlEditorCore} this
24714          */
24715         editorevent: true
24716     });
24717     
24718     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24719     
24720     // defaults : white / black...
24721     this.applyBlacklists();
24722     
24723     
24724     
24725 };
24726
24727
24728 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24729
24730
24731      /**
24732      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24733      */
24734     
24735     owner : false,
24736     
24737      /**
24738      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24739      *                        Roo.resizable.
24740      */
24741     resizable : false,
24742      /**
24743      * @cfg {Number} height (in pixels)
24744      */   
24745     height: 300,
24746    /**
24747      * @cfg {Number} width (in pixels)
24748      */   
24749     width: 500,
24750     
24751     /**
24752      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24753      * 
24754      */
24755     stylesheets: false,
24756     
24757     // id of frame..
24758     frameId: false,
24759     
24760     // private properties
24761     validationEvent : false,
24762     deferHeight: true,
24763     initialized : false,
24764     activated : false,
24765     sourceEditMode : false,
24766     onFocus : Roo.emptyFn,
24767     iframePad:3,
24768     hideMode:'offsets',
24769     
24770     clearUp: true,
24771     
24772     // blacklist + whitelisted elements..
24773     black: false,
24774     white: false,
24775      
24776     
24777
24778     /**
24779      * Protected method that will not generally be called directly. It
24780      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24781      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24782      */
24783     getDocMarkup : function(){
24784         // body styles..
24785         var st = '';
24786         Roo.log(this.stylesheets);
24787         
24788         // inherit styels from page...?? 
24789         if (this.stylesheets === false) {
24790             
24791             Roo.get(document.head).select('style').each(function(node) {
24792                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24793             });
24794             
24795             Roo.get(document.head).select('link').each(function(node) { 
24796                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24797             });
24798             
24799         } else if (!this.stylesheets.length) {
24800                 // simple..
24801                 st = '<style type="text/css">' +
24802                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24803                    '</style>';
24804         } else {
24805             Roo.each(this.stylesheets, function(s) {
24806                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24807             });
24808             
24809         }
24810         
24811         st +=  '<style type="text/css">' +
24812             'IMG { cursor: pointer } ' +
24813         '</style>';
24814
24815         
24816         return '<html><head>' + st  +
24817             //<style type="text/css">' +
24818             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24819             //'</style>' +
24820             ' </head><body class="roo-htmleditor-body"></body></html>';
24821     },
24822
24823     // private
24824     onRender : function(ct, position)
24825     {
24826         var _t = this;
24827         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24828         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24829         
24830         
24831         this.el.dom.style.border = '0 none';
24832         this.el.dom.setAttribute('tabIndex', -1);
24833         this.el.addClass('x-hidden hide');
24834         
24835         
24836         
24837         if(Roo.isIE){ // fix IE 1px bogus margin
24838             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24839         }
24840        
24841         
24842         this.frameId = Roo.id();
24843         
24844          
24845         
24846         var iframe = this.owner.wrap.createChild({
24847             tag: 'iframe',
24848             cls: 'form-control', // bootstrap..
24849             id: this.frameId,
24850             name: this.frameId,
24851             frameBorder : 'no',
24852             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24853         }, this.el
24854         );
24855         
24856         
24857         this.iframe = iframe.dom;
24858
24859          this.assignDocWin();
24860         
24861         this.doc.designMode = 'on';
24862        
24863         this.doc.open();
24864         this.doc.write(this.getDocMarkup());
24865         this.doc.close();
24866
24867         
24868         var task = { // must defer to wait for browser to be ready
24869             run : function(){
24870                 //console.log("run task?" + this.doc.readyState);
24871                 this.assignDocWin();
24872                 if(this.doc.body || this.doc.readyState == 'complete'){
24873                     try {
24874                         this.doc.designMode="on";
24875                     } catch (e) {
24876                         return;
24877                     }
24878                     Roo.TaskMgr.stop(task);
24879                     this.initEditor.defer(10, this);
24880                 }
24881             },
24882             interval : 10,
24883             duration: 10000,
24884             scope: this
24885         };
24886         Roo.TaskMgr.start(task);
24887
24888         
24889          
24890     },
24891
24892     // private
24893     onResize : function(w, h)
24894     {
24895          Roo.log('resize: ' +w + ',' + h );
24896         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24897         if(!this.iframe){
24898             return;
24899         }
24900         if(typeof w == 'number'){
24901             
24902             this.iframe.style.width = w + 'px';
24903         }
24904         if(typeof h == 'number'){
24905             
24906             this.iframe.style.height = h + 'px';
24907             if(this.doc){
24908                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24909             }
24910         }
24911         
24912     },
24913
24914     /**
24915      * Toggles the editor between standard and source edit mode.
24916      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24917      */
24918     toggleSourceEdit : function(sourceEditMode){
24919         
24920         this.sourceEditMode = sourceEditMode === true;
24921         
24922         if(this.sourceEditMode){
24923  
24924             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24925             
24926         }else{
24927             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24928             //this.iframe.className = '';
24929             this.deferFocus();
24930         }
24931         //this.setSize(this.owner.wrap.getSize());
24932         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24933     },
24934
24935     
24936   
24937
24938     /**
24939      * Protected method that will not generally be called directly. If you need/want
24940      * custom HTML cleanup, this is the method you should override.
24941      * @param {String} html The HTML to be cleaned
24942      * return {String} The cleaned HTML
24943      */
24944     cleanHtml : function(html){
24945         html = String(html);
24946         if(html.length > 5){
24947             if(Roo.isSafari){ // strip safari nonsense
24948                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24949             }
24950         }
24951         if(html == '&nbsp;'){
24952             html = '';
24953         }
24954         return html;
24955     },
24956
24957     /**
24958      * HTML Editor -> Textarea
24959      * Protected method that will not generally be called directly. Syncs the contents
24960      * of the editor iframe with the textarea.
24961      */
24962     syncValue : function(){
24963         if(this.initialized){
24964             var bd = (this.doc.body || this.doc.documentElement);
24965             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24966             var html = bd.innerHTML;
24967             if(Roo.isSafari){
24968                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24969                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24970                 if(m && m[1]){
24971                     html = '<div style="'+m[0]+'">' + html + '</div>';
24972                 }
24973             }
24974             html = this.cleanHtml(html);
24975             // fix up the special chars.. normaly like back quotes in word...
24976             // however we do not want to do this with chinese..
24977             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24978                 var cc = b.charCodeAt();
24979                 if (
24980                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24981                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24982                     (cc >= 0xf900 && cc < 0xfb00 )
24983                 ) {
24984                         return b;
24985                 }
24986                 return "&#"+cc+";" 
24987             });
24988             if(this.owner.fireEvent('beforesync', this, html) !== false){
24989                 this.el.dom.value = html;
24990                 this.owner.fireEvent('sync', this, html);
24991             }
24992         }
24993     },
24994
24995     /**
24996      * Protected method that will not generally be called directly. Pushes the value of the textarea
24997      * into the iframe editor.
24998      */
24999     pushValue : function(){
25000         if(this.initialized){
25001             var v = this.el.dom.value.trim();
25002             
25003 //            if(v.length < 1){
25004 //                v = '&#160;';
25005 //            }
25006             
25007             if(this.owner.fireEvent('beforepush', this, v) !== false){
25008                 var d = (this.doc.body || this.doc.documentElement);
25009                 d.innerHTML = v;
25010                 this.cleanUpPaste();
25011                 this.el.dom.value = d.innerHTML;
25012                 this.owner.fireEvent('push', this, v);
25013             }
25014         }
25015     },
25016
25017     // private
25018     deferFocus : function(){
25019         this.focus.defer(10, this);
25020     },
25021
25022     // doc'ed in Field
25023     focus : function(){
25024         if(this.win && !this.sourceEditMode){
25025             this.win.focus();
25026         }else{
25027             this.el.focus();
25028         }
25029     },
25030     
25031     assignDocWin: function()
25032     {
25033         var iframe = this.iframe;
25034         
25035          if(Roo.isIE){
25036             this.doc = iframe.contentWindow.document;
25037             this.win = iframe.contentWindow;
25038         } else {
25039 //            if (!Roo.get(this.frameId)) {
25040 //                return;
25041 //            }
25042 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25043 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25044             
25045             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25046                 return;
25047             }
25048             
25049             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25050             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25051         }
25052     },
25053     
25054     // private
25055     initEditor : function(){
25056         //console.log("INIT EDITOR");
25057         this.assignDocWin();
25058         
25059         
25060         
25061         this.doc.designMode="on";
25062         this.doc.open();
25063         this.doc.write(this.getDocMarkup());
25064         this.doc.close();
25065         
25066         var dbody = (this.doc.body || this.doc.documentElement);
25067         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25068         // this copies styles from the containing element into thsi one..
25069         // not sure why we need all of this..
25070         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25071         
25072         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25073         //ss['background-attachment'] = 'fixed'; // w3c
25074         dbody.bgProperties = 'fixed'; // ie
25075         //Roo.DomHelper.applyStyles(dbody, ss);
25076         Roo.EventManager.on(this.doc, {
25077             //'mousedown': this.onEditorEvent,
25078             'mouseup': this.onEditorEvent,
25079             'dblclick': this.onEditorEvent,
25080             'click': this.onEditorEvent,
25081             'keyup': this.onEditorEvent,
25082             buffer:100,
25083             scope: this
25084         });
25085         if(Roo.isGecko){
25086             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25087         }
25088         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25089             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25090         }
25091         this.initialized = true;
25092
25093         this.owner.fireEvent('initialize', this);
25094         this.pushValue();
25095     },
25096
25097     // private
25098     onDestroy : function(){
25099         
25100         
25101         
25102         if(this.rendered){
25103             
25104             //for (var i =0; i < this.toolbars.length;i++) {
25105             //    // fixme - ask toolbars for heights?
25106             //    this.toolbars[i].onDestroy();
25107            // }
25108             
25109             //this.wrap.dom.innerHTML = '';
25110             //this.wrap.remove();
25111         }
25112     },
25113
25114     // private
25115     onFirstFocus : function(){
25116         
25117         this.assignDocWin();
25118         
25119         
25120         this.activated = true;
25121          
25122     
25123         if(Roo.isGecko){ // prevent silly gecko errors
25124             this.win.focus();
25125             var s = this.win.getSelection();
25126             if(!s.focusNode || s.focusNode.nodeType != 3){
25127                 var r = s.getRangeAt(0);
25128                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25129                 r.collapse(true);
25130                 this.deferFocus();
25131             }
25132             try{
25133                 this.execCmd('useCSS', true);
25134                 this.execCmd('styleWithCSS', false);
25135             }catch(e){}
25136         }
25137         this.owner.fireEvent('activate', this);
25138     },
25139
25140     // private
25141     adjustFont: function(btn){
25142         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25143         //if(Roo.isSafari){ // safari
25144         //    adjust *= 2;
25145        // }
25146         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25147         if(Roo.isSafari){ // safari
25148             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25149             v =  (v < 10) ? 10 : v;
25150             v =  (v > 48) ? 48 : v;
25151             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25152             
25153         }
25154         
25155         
25156         v = Math.max(1, v+adjust);
25157         
25158         this.execCmd('FontSize', v  );
25159     },
25160
25161     onEditorEvent : function(e){
25162         this.owner.fireEvent('editorevent', this, e);
25163       //  this.updateToolbar();
25164         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25165     },
25166
25167     insertTag : function(tg)
25168     {
25169         // could be a bit smarter... -> wrap the current selected tRoo..
25170         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25171             
25172             range = this.createRange(this.getSelection());
25173             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25174             wrappingNode.appendChild(range.extractContents());
25175             range.insertNode(wrappingNode);
25176
25177             return;
25178             
25179             
25180             
25181         }
25182         this.execCmd("formatblock",   tg);
25183         
25184     },
25185     
25186     insertText : function(txt)
25187     {
25188         
25189         
25190         var range = this.createRange();
25191         range.deleteContents();
25192                //alert(Sender.getAttribute('label'));
25193                
25194         range.insertNode(this.doc.createTextNode(txt));
25195     } ,
25196     
25197      
25198
25199     /**
25200      * Executes a Midas editor command on the editor document and performs necessary focus and
25201      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25202      * @param {String} cmd The Midas command
25203      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25204      */
25205     relayCmd : function(cmd, value){
25206         this.win.focus();
25207         this.execCmd(cmd, value);
25208         this.owner.fireEvent('editorevent', this);
25209         //this.updateToolbar();
25210         this.owner.deferFocus();
25211     },
25212
25213     /**
25214      * Executes a Midas editor command directly on the editor document.
25215      * For visual commands, you should use {@link #relayCmd} instead.
25216      * <b>This should only be called after the editor is initialized.</b>
25217      * @param {String} cmd The Midas command
25218      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25219      */
25220     execCmd : function(cmd, value){
25221         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25222         this.syncValue();
25223     },
25224  
25225  
25226    
25227     /**
25228      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25229      * to insert tRoo.
25230      * @param {String} text | dom node.. 
25231      */
25232     insertAtCursor : function(text)
25233     {
25234         
25235         
25236         
25237         if(!this.activated){
25238             return;
25239         }
25240         /*
25241         if(Roo.isIE){
25242             this.win.focus();
25243             var r = this.doc.selection.createRange();
25244             if(r){
25245                 r.collapse(true);
25246                 r.pasteHTML(text);
25247                 this.syncValue();
25248                 this.deferFocus();
25249             
25250             }
25251             return;
25252         }
25253         */
25254         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25255             this.win.focus();
25256             
25257             
25258             // from jquery ui (MIT licenced)
25259             var range, node;
25260             var win = this.win;
25261             
25262             if (win.getSelection && win.getSelection().getRangeAt) {
25263                 range = win.getSelection().getRangeAt(0);
25264                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25265                 range.insertNode(node);
25266             } else if (win.document.selection && win.document.selection.createRange) {
25267                 // no firefox support
25268                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25269                 win.document.selection.createRange().pasteHTML(txt);
25270             } else {
25271                 // no firefox support
25272                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25273                 this.execCmd('InsertHTML', txt);
25274             } 
25275             
25276             this.syncValue();
25277             
25278             this.deferFocus();
25279         }
25280     },
25281  // private
25282     mozKeyPress : function(e){
25283         if(e.ctrlKey){
25284             var c = e.getCharCode(), cmd;
25285           
25286             if(c > 0){
25287                 c = String.fromCharCode(c).toLowerCase();
25288                 switch(c){
25289                     case 'b':
25290                         cmd = 'bold';
25291                         break;
25292                     case 'i':
25293                         cmd = 'italic';
25294                         break;
25295                     
25296                     case 'u':
25297                         cmd = 'underline';
25298                         break;
25299                     
25300                     case 'v':
25301                         this.cleanUpPaste.defer(100, this);
25302                         return;
25303                         
25304                 }
25305                 if(cmd){
25306                     this.win.focus();
25307                     this.execCmd(cmd);
25308                     this.deferFocus();
25309                     e.preventDefault();
25310                 }
25311                 
25312             }
25313         }
25314     },
25315
25316     // private
25317     fixKeys : function(){ // load time branching for fastest keydown performance
25318         if(Roo.isIE){
25319             return function(e){
25320                 var k = e.getKey(), r;
25321                 if(k == e.TAB){
25322                     e.stopEvent();
25323                     r = this.doc.selection.createRange();
25324                     if(r){
25325                         r.collapse(true);
25326                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25327                         this.deferFocus();
25328                     }
25329                     return;
25330                 }
25331                 
25332                 if(k == e.ENTER){
25333                     r = this.doc.selection.createRange();
25334                     if(r){
25335                         var target = r.parentElement();
25336                         if(!target || target.tagName.toLowerCase() != 'li'){
25337                             e.stopEvent();
25338                             r.pasteHTML('<br />');
25339                             r.collapse(false);
25340                             r.select();
25341                         }
25342                     }
25343                 }
25344                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25345                     this.cleanUpPaste.defer(100, this);
25346                     return;
25347                 }
25348                 
25349                 
25350             };
25351         }else if(Roo.isOpera){
25352             return function(e){
25353                 var k = e.getKey();
25354                 if(k == e.TAB){
25355                     e.stopEvent();
25356                     this.win.focus();
25357                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25358                     this.deferFocus();
25359                 }
25360                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25361                     this.cleanUpPaste.defer(100, this);
25362                     return;
25363                 }
25364                 
25365             };
25366         }else if(Roo.isSafari){
25367             return function(e){
25368                 var k = e.getKey();
25369                 
25370                 if(k == e.TAB){
25371                     e.stopEvent();
25372                     this.execCmd('InsertText','\t');
25373                     this.deferFocus();
25374                     return;
25375                 }
25376                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25377                     this.cleanUpPaste.defer(100, this);
25378                     return;
25379                 }
25380                 
25381              };
25382         }
25383     }(),
25384     
25385     getAllAncestors: function()
25386     {
25387         var p = this.getSelectedNode();
25388         var a = [];
25389         if (!p) {
25390             a.push(p); // push blank onto stack..
25391             p = this.getParentElement();
25392         }
25393         
25394         
25395         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25396             a.push(p);
25397             p = p.parentNode;
25398         }
25399         a.push(this.doc.body);
25400         return a;
25401     },
25402     lastSel : false,
25403     lastSelNode : false,
25404     
25405     
25406     getSelection : function() 
25407     {
25408         this.assignDocWin();
25409         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25410     },
25411     
25412     getSelectedNode: function() 
25413     {
25414         // this may only work on Gecko!!!
25415         
25416         // should we cache this!!!!
25417         
25418         
25419         
25420          
25421         var range = this.createRange(this.getSelection()).cloneRange();
25422         
25423         if (Roo.isIE) {
25424             var parent = range.parentElement();
25425             while (true) {
25426                 var testRange = range.duplicate();
25427                 testRange.moveToElementText(parent);
25428                 if (testRange.inRange(range)) {
25429                     break;
25430                 }
25431                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25432                     break;
25433                 }
25434                 parent = parent.parentElement;
25435             }
25436             return parent;
25437         }
25438         
25439         // is ancestor a text element.
25440         var ac =  range.commonAncestorContainer;
25441         if (ac.nodeType == 3) {
25442             ac = ac.parentNode;
25443         }
25444         
25445         var ar = ac.childNodes;
25446          
25447         var nodes = [];
25448         var other_nodes = [];
25449         var has_other_nodes = false;
25450         for (var i=0;i<ar.length;i++) {
25451             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25452                 continue;
25453             }
25454             // fullly contained node.
25455             
25456             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25457                 nodes.push(ar[i]);
25458                 continue;
25459             }
25460             
25461             // probably selected..
25462             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25463                 other_nodes.push(ar[i]);
25464                 continue;
25465             }
25466             // outer..
25467             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25468                 continue;
25469             }
25470             
25471             
25472             has_other_nodes = true;
25473         }
25474         if (!nodes.length && other_nodes.length) {
25475             nodes= other_nodes;
25476         }
25477         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25478             return false;
25479         }
25480         
25481         return nodes[0];
25482     },
25483     createRange: function(sel)
25484     {
25485         // this has strange effects when using with 
25486         // top toolbar - not sure if it's a great idea.
25487         //this.editor.contentWindow.focus();
25488         if (typeof sel != "undefined") {
25489             try {
25490                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25491             } catch(e) {
25492                 return this.doc.createRange();
25493             }
25494         } else {
25495             return this.doc.createRange();
25496         }
25497     },
25498     getParentElement: function()
25499     {
25500         
25501         this.assignDocWin();
25502         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25503         
25504         var range = this.createRange(sel);
25505          
25506         try {
25507             var p = range.commonAncestorContainer;
25508             while (p.nodeType == 3) { // text node
25509                 p = p.parentNode;
25510             }
25511             return p;
25512         } catch (e) {
25513             return null;
25514         }
25515     
25516     },
25517     /***
25518      *
25519      * Range intersection.. the hard stuff...
25520      *  '-1' = before
25521      *  '0' = hits..
25522      *  '1' = after.
25523      *         [ -- selected range --- ]
25524      *   [fail]                        [fail]
25525      *
25526      *    basically..
25527      *      if end is before start or  hits it. fail.
25528      *      if start is after end or hits it fail.
25529      *
25530      *   if either hits (but other is outside. - then it's not 
25531      *   
25532      *    
25533      **/
25534     
25535     
25536     // @see http://www.thismuchiknow.co.uk/?p=64.
25537     rangeIntersectsNode : function(range, node)
25538     {
25539         var nodeRange = node.ownerDocument.createRange();
25540         try {
25541             nodeRange.selectNode(node);
25542         } catch (e) {
25543             nodeRange.selectNodeContents(node);
25544         }
25545     
25546         var rangeStartRange = range.cloneRange();
25547         rangeStartRange.collapse(true);
25548     
25549         var rangeEndRange = range.cloneRange();
25550         rangeEndRange.collapse(false);
25551     
25552         var nodeStartRange = nodeRange.cloneRange();
25553         nodeStartRange.collapse(true);
25554     
25555         var nodeEndRange = nodeRange.cloneRange();
25556         nodeEndRange.collapse(false);
25557     
25558         return rangeStartRange.compareBoundaryPoints(
25559                  Range.START_TO_START, nodeEndRange) == -1 &&
25560                rangeEndRange.compareBoundaryPoints(
25561                  Range.START_TO_START, nodeStartRange) == 1;
25562         
25563          
25564     },
25565     rangeCompareNode : function(range, node)
25566     {
25567         var nodeRange = node.ownerDocument.createRange();
25568         try {
25569             nodeRange.selectNode(node);
25570         } catch (e) {
25571             nodeRange.selectNodeContents(node);
25572         }
25573         
25574         
25575         range.collapse(true);
25576     
25577         nodeRange.collapse(true);
25578      
25579         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25580         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25581          
25582         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25583         
25584         var nodeIsBefore   =  ss == 1;
25585         var nodeIsAfter    = ee == -1;
25586         
25587         if (nodeIsBefore && nodeIsAfter)
25588             return 0; // outer
25589         if (!nodeIsBefore && nodeIsAfter)
25590             return 1; //right trailed.
25591         
25592         if (nodeIsBefore && !nodeIsAfter)
25593             return 2;  // left trailed.
25594         // fully contined.
25595         return 3;
25596     },
25597
25598     // private? - in a new class?
25599     cleanUpPaste :  function()
25600     {
25601         // cleans up the whole document..
25602         Roo.log('cleanuppaste');
25603         
25604         this.cleanUpChildren(this.doc.body);
25605         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25606         if (clean != this.doc.body.innerHTML) {
25607             this.doc.body.innerHTML = clean;
25608         }
25609         
25610     },
25611     
25612     cleanWordChars : function(input) {// change the chars to hex code
25613         var he = Roo.HtmlEditorCore;
25614         
25615         var output = input;
25616         Roo.each(he.swapCodes, function(sw) { 
25617             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25618             
25619             output = output.replace(swapper, sw[1]);
25620         });
25621         
25622         return output;
25623     },
25624     
25625     
25626     cleanUpChildren : function (n)
25627     {
25628         if (!n.childNodes.length) {
25629             return;
25630         }
25631         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25632            this.cleanUpChild(n.childNodes[i]);
25633         }
25634     },
25635     
25636     
25637         
25638     
25639     cleanUpChild : function (node)
25640     {
25641         var ed = this;
25642         //console.log(node);
25643         if (node.nodeName == "#text") {
25644             // clean up silly Windows -- stuff?
25645             return; 
25646         }
25647         if (node.nodeName == "#comment") {
25648             node.parentNode.removeChild(node);
25649             // clean up silly Windows -- stuff?
25650             return; 
25651         }
25652         var lcname = node.tagName.toLowerCase();
25653         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25654         // whitelist of tags..
25655         
25656         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25657             // remove node.
25658             node.parentNode.removeChild(node);
25659             return;
25660             
25661         }
25662         
25663         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25664         
25665         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25666         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25667         
25668         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25669         //    remove_keep_children = true;
25670         //}
25671         
25672         if (remove_keep_children) {
25673             this.cleanUpChildren(node);
25674             // inserts everything just before this node...
25675             while (node.childNodes.length) {
25676                 var cn = node.childNodes[0];
25677                 node.removeChild(cn);
25678                 node.parentNode.insertBefore(cn, node);
25679             }
25680             node.parentNode.removeChild(node);
25681             return;
25682         }
25683         
25684         if (!node.attributes || !node.attributes.length) {
25685             this.cleanUpChildren(node);
25686             return;
25687         }
25688         
25689         function cleanAttr(n,v)
25690         {
25691             
25692             if (v.match(/^\./) || v.match(/^\//)) {
25693                 return;
25694             }
25695             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25696                 return;
25697             }
25698             if (v.match(/^#/)) {
25699                 return;
25700             }
25701 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25702             node.removeAttribute(n);
25703             
25704         }
25705         
25706         var cwhite = this.cwhite;
25707         var cblack = this.cblack;
25708             
25709         function cleanStyle(n,v)
25710         {
25711             if (v.match(/expression/)) { //XSS?? should we even bother..
25712                 node.removeAttribute(n);
25713                 return;
25714             }
25715             
25716             var parts = v.split(/;/);
25717             var clean = [];
25718             
25719             Roo.each(parts, function(p) {
25720                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25721                 if (!p.length) {
25722                     return true;
25723                 }
25724                 var l = p.split(':').shift().replace(/\s+/g,'');
25725                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25726                 
25727                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25728 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25729                     //node.removeAttribute(n);
25730                     return true;
25731                 }
25732                 //Roo.log()
25733                 // only allow 'c whitelisted system attributes'
25734                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25735 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25736                     //node.removeAttribute(n);
25737                     return true;
25738                 }
25739                 
25740                 
25741                  
25742                 
25743                 clean.push(p);
25744                 return true;
25745             });
25746             if (clean.length) { 
25747                 node.setAttribute(n, clean.join(';'));
25748             } else {
25749                 node.removeAttribute(n);
25750             }
25751             
25752         }
25753         
25754         
25755         for (var i = node.attributes.length-1; i > -1 ; i--) {
25756             var a = node.attributes[i];
25757             //console.log(a);
25758             
25759             if (a.name.toLowerCase().substr(0,2)=='on')  {
25760                 node.removeAttribute(a.name);
25761                 continue;
25762             }
25763             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25764                 node.removeAttribute(a.name);
25765                 continue;
25766             }
25767             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25768                 cleanAttr(a.name,a.value); // fixme..
25769                 continue;
25770             }
25771             if (a.name == 'style') {
25772                 cleanStyle(a.name,a.value);
25773                 continue;
25774             }
25775             /// clean up MS crap..
25776             // tecnically this should be a list of valid class'es..
25777             
25778             
25779             if (a.name == 'class') {
25780                 if (a.value.match(/^Mso/)) {
25781                     node.className = '';
25782                 }
25783                 
25784                 if (a.value.match(/body/)) {
25785                     node.className = '';
25786                 }
25787                 continue;
25788             }
25789             
25790             // style cleanup!?
25791             // class cleanup?
25792             
25793         }
25794         
25795         
25796         this.cleanUpChildren(node);
25797         
25798         
25799     },
25800     /**
25801      * Clean up MS wordisms...
25802      */
25803     cleanWord : function(node)
25804     {
25805         var _t = this;
25806         var cleanWordChildren = function()
25807         {
25808             if (!node.childNodes.length) {
25809                 return;
25810             }
25811             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25812                _t.cleanWord(node.childNodes[i]);
25813             }
25814         }
25815         
25816         
25817         if (!node) {
25818             this.cleanWord(this.doc.body);
25819             return;
25820         }
25821         if (node.nodeName == "#text") {
25822             // clean up silly Windows -- stuff?
25823             return; 
25824         }
25825         if (node.nodeName == "#comment") {
25826             node.parentNode.removeChild(node);
25827             // clean up silly Windows -- stuff?
25828             return; 
25829         }
25830         
25831         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25832             node.parentNode.removeChild(node);
25833             return;
25834         }
25835         
25836         // remove - but keep children..
25837         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25838             while (node.childNodes.length) {
25839                 var cn = node.childNodes[0];
25840                 node.removeChild(cn);
25841                 node.parentNode.insertBefore(cn, node);
25842             }
25843             node.parentNode.removeChild(node);
25844             cleanWordChildren();
25845             return;
25846         }
25847         // clean styles
25848         if (node.className.length) {
25849             
25850             var cn = node.className.split(/\W+/);
25851             var cna = [];
25852             Roo.each(cn, function(cls) {
25853                 if (cls.match(/Mso[a-zA-Z]+/)) {
25854                     return;
25855                 }
25856                 cna.push(cls);
25857             });
25858             node.className = cna.length ? cna.join(' ') : '';
25859             if (!cna.length) {
25860                 node.removeAttribute("class");
25861             }
25862         }
25863         
25864         if (node.hasAttribute("lang")) {
25865             node.removeAttribute("lang");
25866         }
25867         
25868         if (node.hasAttribute("style")) {
25869             
25870             var styles = node.getAttribute("style").split(";");
25871             var nstyle = [];
25872             Roo.each(styles, function(s) {
25873                 if (!s.match(/:/)) {
25874                     return;
25875                 }
25876                 var kv = s.split(":");
25877                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25878                     return;
25879                 }
25880                 // what ever is left... we allow.
25881                 nstyle.push(s);
25882             });
25883             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25884             if (!nstyle.length) {
25885                 node.removeAttribute('style');
25886             }
25887         }
25888         
25889         cleanWordChildren();
25890         
25891         
25892     },
25893     domToHTML : function(currentElement, depth, nopadtext) {
25894         
25895         depth = depth || 0;
25896         nopadtext = nopadtext || false;
25897     
25898         if (!currentElement) {
25899             return this.domToHTML(this.doc.body);
25900         }
25901         
25902         //Roo.log(currentElement);
25903         var j;
25904         var allText = false;
25905         var nodeName = currentElement.nodeName;
25906         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25907         
25908         if  (nodeName == '#text') {
25909             return currentElement.nodeValue;
25910         }
25911         
25912         
25913         var ret = '';
25914         if (nodeName != 'BODY') {
25915              
25916             var i = 0;
25917             // Prints the node tagName, such as <A>, <IMG>, etc
25918             if (tagName) {
25919                 var attr = [];
25920                 for(i = 0; i < currentElement.attributes.length;i++) {
25921                     // quoting?
25922                     var aname = currentElement.attributes.item(i).name;
25923                     if (!currentElement.attributes.item(i).value.length) {
25924                         continue;
25925                     }
25926                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25927                 }
25928                 
25929                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25930             } 
25931             else {
25932                 
25933                 // eack
25934             }
25935         } else {
25936             tagName = false;
25937         }
25938         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25939             return ret;
25940         }
25941         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25942             nopadtext = true;
25943         }
25944         
25945         
25946         // Traverse the tree
25947         i = 0;
25948         var currentElementChild = currentElement.childNodes.item(i);
25949         var allText = true;
25950         var innerHTML  = '';
25951         lastnode = '';
25952         while (currentElementChild) {
25953             // Formatting code (indent the tree so it looks nice on the screen)
25954             var nopad = nopadtext;
25955             if (lastnode == 'SPAN') {
25956                 nopad  = true;
25957             }
25958             // text
25959             if  (currentElementChild.nodeName == '#text') {
25960                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25961                 if (!nopad && toadd.length > 80) {
25962                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25963                 }
25964                 innerHTML  += toadd;
25965                 
25966                 i++;
25967                 currentElementChild = currentElement.childNodes.item(i);
25968                 lastNode = '';
25969                 continue;
25970             }
25971             allText = false;
25972             
25973             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25974                 
25975             // Recursively traverse the tree structure of the child node
25976             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25977             lastnode = currentElementChild.nodeName;
25978             i++;
25979             currentElementChild=currentElement.childNodes.item(i);
25980         }
25981         
25982         ret += innerHTML;
25983         
25984         if (!allText) {
25985                 // The remaining code is mostly for formatting the tree
25986             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25987         }
25988         
25989         
25990         if (tagName) {
25991             ret+= "</"+tagName+">";
25992         }
25993         return ret;
25994         
25995     },
25996         
25997     applyBlacklists : function()
25998     {
25999         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26000         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26001         
26002         this.white = [];
26003         this.black = [];
26004         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26005             if (b.indexOf(tag) > -1) {
26006                 return;
26007             }
26008             this.white.push(tag);
26009             
26010         }, this);
26011         
26012         Roo.each(w, function(tag) {
26013             if (b.indexOf(tag) > -1) {
26014                 return;
26015             }
26016             if (this.white.indexOf(tag) > -1) {
26017                 return;
26018             }
26019             this.white.push(tag);
26020             
26021         }, this);
26022         
26023         
26024         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26025             if (w.indexOf(tag) > -1) {
26026                 return;
26027             }
26028             this.black.push(tag);
26029             
26030         }, this);
26031         
26032         Roo.each(b, function(tag) {
26033             if (w.indexOf(tag) > -1) {
26034                 return;
26035             }
26036             if (this.black.indexOf(tag) > -1) {
26037                 return;
26038             }
26039             this.black.push(tag);
26040             
26041         }, this);
26042         
26043         
26044         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26045         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26046         
26047         this.cwhite = [];
26048         this.cblack = [];
26049         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26050             if (b.indexOf(tag) > -1) {
26051                 return;
26052             }
26053             this.cwhite.push(tag);
26054             
26055         }, this);
26056         
26057         Roo.each(w, function(tag) {
26058             if (b.indexOf(tag) > -1) {
26059                 return;
26060             }
26061             if (this.cwhite.indexOf(tag) > -1) {
26062                 return;
26063             }
26064             this.cwhite.push(tag);
26065             
26066         }, this);
26067         
26068         
26069         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26070             if (w.indexOf(tag) > -1) {
26071                 return;
26072             }
26073             this.cblack.push(tag);
26074             
26075         }, this);
26076         
26077         Roo.each(b, function(tag) {
26078             if (w.indexOf(tag) > -1) {
26079                 return;
26080             }
26081             if (this.cblack.indexOf(tag) > -1) {
26082                 return;
26083             }
26084             this.cblack.push(tag);
26085             
26086         }, this);
26087     }
26088     
26089     // hide stuff that is not compatible
26090     /**
26091      * @event blur
26092      * @hide
26093      */
26094     /**
26095      * @event change
26096      * @hide
26097      */
26098     /**
26099      * @event focus
26100      * @hide
26101      */
26102     /**
26103      * @event specialkey
26104      * @hide
26105      */
26106     /**
26107      * @cfg {String} fieldClass @hide
26108      */
26109     /**
26110      * @cfg {String} focusClass @hide
26111      */
26112     /**
26113      * @cfg {String} autoCreate @hide
26114      */
26115     /**
26116      * @cfg {String} inputType @hide
26117      */
26118     /**
26119      * @cfg {String} invalidClass @hide
26120      */
26121     /**
26122      * @cfg {String} invalidText @hide
26123      */
26124     /**
26125      * @cfg {String} msgFx @hide
26126      */
26127     /**
26128      * @cfg {String} validateOnBlur @hide
26129      */
26130 });
26131
26132 Roo.HtmlEditorCore.white = [
26133         'area', 'br', 'img', 'input', 'hr', 'wbr',
26134         
26135        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26136        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26137        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26138        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26139        'table',   'ul',         'xmp', 
26140        
26141        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26142       'thead',   'tr', 
26143      
26144       'dir', 'menu', 'ol', 'ul', 'dl',
26145        
26146       'embed',  'object'
26147 ];
26148
26149
26150 Roo.HtmlEditorCore.black = [
26151     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26152         'applet', // 
26153         'base',   'basefont', 'bgsound', 'blink',  'body', 
26154         'frame',  'frameset', 'head',    'html',   'ilayer', 
26155         'iframe', 'layer',  'link',     'meta',    'object',   
26156         'script', 'style' ,'title',  'xml' // clean later..
26157 ];
26158 Roo.HtmlEditorCore.clean = [
26159     'script', 'style', 'title', 'xml'
26160 ];
26161 Roo.HtmlEditorCore.remove = [
26162     'font'
26163 ];
26164 // attributes..
26165
26166 Roo.HtmlEditorCore.ablack = [
26167     'on'
26168 ];
26169     
26170 Roo.HtmlEditorCore.aclean = [ 
26171     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26172 ];
26173
26174 // protocols..
26175 Roo.HtmlEditorCore.pwhite= [
26176         'http',  'https',  'mailto'
26177 ];
26178
26179 // white listed style attributes.
26180 Roo.HtmlEditorCore.cwhite= [
26181       //  'text-align', /// default is to allow most things..
26182       
26183          
26184 //        'font-size'//??
26185 ];
26186
26187 // black listed style attributes.
26188 Roo.HtmlEditorCore.cblack= [
26189       //  'font-size' -- this can be set by the project 
26190 ];
26191
26192
26193 Roo.HtmlEditorCore.swapCodes   =[ 
26194     [    8211, "--" ], 
26195     [    8212, "--" ], 
26196     [    8216,  "'" ],  
26197     [    8217, "'" ],  
26198     [    8220, '"' ],  
26199     [    8221, '"' ],  
26200     [    8226, "*" ],  
26201     [    8230, "..." ]
26202 ]; 
26203
26204     //<script type="text/javascript">
26205
26206 /*
26207  * Ext JS Library 1.1.1
26208  * Copyright(c) 2006-2007, Ext JS, LLC.
26209  * Licence LGPL
26210  * 
26211  */
26212  
26213  
26214 Roo.form.HtmlEditor = function(config){
26215     
26216     
26217     
26218     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26219     
26220     if (!this.toolbars) {
26221         this.toolbars = [];
26222     }
26223     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26224     
26225     
26226 };
26227
26228 /**
26229  * @class Roo.form.HtmlEditor
26230  * @extends Roo.form.Field
26231  * Provides a lightweight HTML Editor component.
26232  *
26233  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26234  * 
26235  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26236  * supported by this editor.</b><br/><br/>
26237  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26238  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26239  */
26240 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26241     /**
26242      * @cfg {Boolean} clearUp
26243      */
26244     clearUp : true,
26245       /**
26246      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26247      */
26248     toolbars : false,
26249    
26250      /**
26251      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26252      *                        Roo.resizable.
26253      */
26254     resizable : false,
26255      /**
26256      * @cfg {Number} height (in pixels)
26257      */   
26258     height: 300,
26259    /**
26260      * @cfg {Number} width (in pixels)
26261      */   
26262     width: 500,
26263     
26264     /**
26265      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26266      * 
26267      */
26268     stylesheets: false,
26269     
26270     
26271      /**
26272      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26273      * 
26274      */
26275     cblack: false,
26276     /**
26277      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26278      * 
26279      */
26280     cwhite: false,
26281     
26282      /**
26283      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26284      * 
26285      */
26286     black: false,
26287     /**
26288      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26289      * 
26290      */
26291     white: false,
26292     
26293     // id of frame..
26294     frameId: false,
26295     
26296     // private properties
26297     validationEvent : false,
26298     deferHeight: true,
26299     initialized : false,
26300     activated : false,
26301     
26302     onFocus : Roo.emptyFn,
26303     iframePad:3,
26304     hideMode:'offsets',
26305     
26306     actionMode : 'container', // defaults to hiding it...
26307     
26308     defaultAutoCreate : { // modified by initCompnoent..
26309         tag: "textarea",
26310         style:"width:500px;height:300px;",
26311         autocomplete: "off"
26312     },
26313
26314     // private
26315     initComponent : function(){
26316         this.addEvents({
26317             /**
26318              * @event initialize
26319              * Fires when the editor is fully initialized (including the iframe)
26320              * @param {HtmlEditor} this
26321              */
26322             initialize: true,
26323             /**
26324              * @event activate
26325              * Fires when the editor is first receives the focus. Any insertion must wait
26326              * until after this event.
26327              * @param {HtmlEditor} this
26328              */
26329             activate: true,
26330              /**
26331              * @event beforesync
26332              * Fires before the textarea is updated with content from the editor iframe. Return false
26333              * to cancel the sync.
26334              * @param {HtmlEditor} this
26335              * @param {String} html
26336              */
26337             beforesync: true,
26338              /**
26339              * @event beforepush
26340              * Fires before the iframe editor is updated with content from the textarea. Return false
26341              * to cancel the push.
26342              * @param {HtmlEditor} this
26343              * @param {String} html
26344              */
26345             beforepush: true,
26346              /**
26347              * @event sync
26348              * Fires when the textarea is updated with content from the editor iframe.
26349              * @param {HtmlEditor} this
26350              * @param {String} html
26351              */
26352             sync: true,
26353              /**
26354              * @event push
26355              * Fires when the iframe editor is updated with content from the textarea.
26356              * @param {HtmlEditor} this
26357              * @param {String} html
26358              */
26359             push: true,
26360              /**
26361              * @event editmodechange
26362              * Fires when the editor switches edit modes
26363              * @param {HtmlEditor} this
26364              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26365              */
26366             editmodechange: true,
26367             /**
26368              * @event editorevent
26369              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26370              * @param {HtmlEditor} this
26371              */
26372             editorevent: true,
26373             /**
26374              * @event firstfocus
26375              * Fires when on first focus - needed by toolbars..
26376              * @param {HtmlEditor} this
26377              */
26378             firstfocus: true,
26379             /**
26380              * @event autosave
26381              * Auto save the htmlEditor value as a file into Events
26382              * @param {HtmlEditor} this
26383              */
26384             autosave: true,
26385             /**
26386              * @event savedpreview
26387              * preview the saved version of htmlEditor
26388              * @param {HtmlEditor} this
26389              */
26390             savedpreview: true
26391         });
26392         this.defaultAutoCreate =  {
26393             tag: "textarea",
26394             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26395             autocomplete: "off"
26396         };
26397     },
26398
26399     /**
26400      * Protected method that will not generally be called directly. It
26401      * is called when the editor creates its toolbar. Override this method if you need to
26402      * add custom toolbar buttons.
26403      * @param {HtmlEditor} editor
26404      */
26405     createToolbar : function(editor){
26406         Roo.log("create toolbars");
26407         if (!editor.toolbars || !editor.toolbars.length) {
26408             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26409         }
26410         
26411         for (var i =0 ; i < editor.toolbars.length;i++) {
26412             editor.toolbars[i] = Roo.factory(
26413                     typeof(editor.toolbars[i]) == 'string' ?
26414                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26415                 Roo.form.HtmlEditor);
26416             editor.toolbars[i].init(editor);
26417         }
26418          
26419         
26420     },
26421
26422      
26423     // private
26424     onRender : function(ct, position)
26425     {
26426         var _t = this;
26427         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26428         
26429         this.wrap = this.el.wrap({
26430             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26431         });
26432         
26433         this.editorcore.onRender(ct, position);
26434          
26435         if (this.resizable) {
26436             this.resizeEl = new Roo.Resizable(this.wrap, {
26437                 pinned : true,
26438                 wrap: true,
26439                 dynamic : true,
26440                 minHeight : this.height,
26441                 height: this.height,
26442                 handles : this.resizable,
26443                 width: this.width,
26444                 listeners : {
26445                     resize : function(r, w, h) {
26446                         _t.onResize(w,h); // -something
26447                     }
26448                 }
26449             });
26450             
26451         }
26452         this.createToolbar(this);
26453        
26454         
26455         if(!this.width){
26456             this.setSize(this.wrap.getSize());
26457         }
26458         if (this.resizeEl) {
26459             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26460             // should trigger onReize..
26461         }
26462         
26463 //        if(this.autosave && this.w){
26464 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26465 //        }
26466     },
26467
26468     // private
26469     onResize : function(w, h)
26470     {
26471         //Roo.log('resize: ' +w + ',' + h );
26472         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26473         var ew = false;
26474         var eh = false;
26475         
26476         if(this.el ){
26477             if(typeof w == 'number'){
26478                 var aw = w - this.wrap.getFrameWidth('lr');
26479                 this.el.setWidth(this.adjustWidth('textarea', aw));
26480                 ew = aw;
26481             }
26482             if(typeof h == 'number'){
26483                 var tbh = 0;
26484                 for (var i =0; i < this.toolbars.length;i++) {
26485                     // fixme - ask toolbars for heights?
26486                     tbh += this.toolbars[i].tb.el.getHeight();
26487                     if (this.toolbars[i].footer) {
26488                         tbh += this.toolbars[i].footer.el.getHeight();
26489                     }
26490                 }
26491                 
26492                 
26493                 
26494                 
26495                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26496                 ah -= 5; // knock a few pixes off for look..
26497                 this.el.setHeight(this.adjustWidth('textarea', ah));
26498                 var eh = ah;
26499             }
26500         }
26501         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26502         this.editorcore.onResize(ew,eh);
26503         
26504     },
26505
26506     /**
26507      * Toggles the editor between standard and source edit mode.
26508      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26509      */
26510     toggleSourceEdit : function(sourceEditMode)
26511     {
26512         this.editorcore.toggleSourceEdit(sourceEditMode);
26513         
26514         if(this.editorcore.sourceEditMode){
26515             Roo.log('editor - showing textarea');
26516             
26517 //            Roo.log('in');
26518 //            Roo.log(this.syncValue());
26519             this.editorcore.syncValue();
26520             this.el.removeClass('x-hidden');
26521             this.el.dom.removeAttribute('tabIndex');
26522             this.el.focus();
26523         }else{
26524             Roo.log('editor - hiding textarea');
26525 //            Roo.log('out')
26526 //            Roo.log(this.pushValue()); 
26527             this.editorcore.pushValue();
26528             
26529             this.el.addClass('x-hidden');
26530             this.el.dom.setAttribute('tabIndex', -1);
26531             //this.deferFocus();
26532         }
26533          
26534         this.setSize(this.wrap.getSize());
26535         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26536     },
26537  
26538     // private (for BoxComponent)
26539     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26540
26541     // private (for BoxComponent)
26542     getResizeEl : function(){
26543         return this.wrap;
26544     },
26545
26546     // private (for BoxComponent)
26547     getPositionEl : function(){
26548         return this.wrap;
26549     },
26550
26551     // private
26552     initEvents : function(){
26553         this.originalValue = this.getValue();
26554     },
26555
26556     /**
26557      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26558      * @method
26559      */
26560     markInvalid : Roo.emptyFn,
26561     /**
26562      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26563      * @method
26564      */
26565     clearInvalid : Roo.emptyFn,
26566
26567     setValue : function(v){
26568         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26569         this.editorcore.pushValue();
26570     },
26571
26572      
26573     // private
26574     deferFocus : function(){
26575         this.focus.defer(10, this);
26576     },
26577
26578     // doc'ed in Field
26579     focus : function(){
26580         this.editorcore.focus();
26581         
26582     },
26583       
26584
26585     // private
26586     onDestroy : function(){
26587         
26588         
26589         
26590         if(this.rendered){
26591             
26592             for (var i =0; i < this.toolbars.length;i++) {
26593                 // fixme - ask toolbars for heights?
26594                 this.toolbars[i].onDestroy();
26595             }
26596             
26597             this.wrap.dom.innerHTML = '';
26598             this.wrap.remove();
26599         }
26600     },
26601
26602     // private
26603     onFirstFocus : function(){
26604         //Roo.log("onFirstFocus");
26605         this.editorcore.onFirstFocus();
26606          for (var i =0; i < this.toolbars.length;i++) {
26607             this.toolbars[i].onFirstFocus();
26608         }
26609         
26610     },
26611     
26612     // private
26613     syncValue : function()
26614     {
26615         this.editorcore.syncValue();
26616     },
26617     
26618     pushValue : function()
26619     {
26620         this.editorcore.pushValue();
26621     }
26622      
26623     
26624     // hide stuff that is not compatible
26625     /**
26626      * @event blur
26627      * @hide
26628      */
26629     /**
26630      * @event change
26631      * @hide
26632      */
26633     /**
26634      * @event focus
26635      * @hide
26636      */
26637     /**
26638      * @event specialkey
26639      * @hide
26640      */
26641     /**
26642      * @cfg {String} fieldClass @hide
26643      */
26644     /**
26645      * @cfg {String} focusClass @hide
26646      */
26647     /**
26648      * @cfg {String} autoCreate @hide
26649      */
26650     /**
26651      * @cfg {String} inputType @hide
26652      */
26653     /**
26654      * @cfg {String} invalidClass @hide
26655      */
26656     /**
26657      * @cfg {String} invalidText @hide
26658      */
26659     /**
26660      * @cfg {String} msgFx @hide
26661      */
26662     /**
26663      * @cfg {String} validateOnBlur @hide
26664      */
26665 });
26666  
26667     // <script type="text/javascript">
26668 /*
26669  * Based on
26670  * Ext JS Library 1.1.1
26671  * Copyright(c) 2006-2007, Ext JS, LLC.
26672  *  
26673  
26674  */
26675
26676 /**
26677  * @class Roo.form.HtmlEditorToolbar1
26678  * Basic Toolbar
26679  * 
26680  * Usage:
26681  *
26682  new Roo.form.HtmlEditor({
26683     ....
26684     toolbars : [
26685         new Roo.form.HtmlEditorToolbar1({
26686             disable : { fonts: 1 , format: 1, ..., ... , ...],
26687             btns : [ .... ]
26688         })
26689     }
26690      
26691  * 
26692  * @cfg {Object} disable List of elements to disable..
26693  * @cfg {Array} btns List of additional buttons.
26694  * 
26695  * 
26696  * NEEDS Extra CSS? 
26697  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26698  */
26699  
26700 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26701 {
26702     
26703     Roo.apply(this, config);
26704     
26705     // default disabled, based on 'good practice'..
26706     this.disable = this.disable || {};
26707     Roo.applyIf(this.disable, {
26708         fontSize : true,
26709         colors : true,
26710         specialElements : true
26711     });
26712     
26713     
26714     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26715     // dont call parent... till later.
26716 }
26717
26718 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26719     
26720     tb: false,
26721     
26722     rendered: false,
26723     
26724     editor : false,
26725     editorcore : false,
26726     /**
26727      * @cfg {Object} disable  List of toolbar elements to disable
26728          
26729      */
26730     disable : false,
26731     
26732     
26733      /**
26734      * @cfg {String} createLinkText The default text for the create link prompt
26735      */
26736     createLinkText : 'Please enter the URL for the link:',
26737     /**
26738      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26739      */
26740     defaultLinkValue : 'http:/'+'/',
26741    
26742     
26743       /**
26744      * @cfg {Array} fontFamilies An array of available font families
26745      */
26746     fontFamilies : [
26747         'Arial',
26748         'Courier New',
26749         'Tahoma',
26750         'Times New Roman',
26751         'Verdana'
26752     ],
26753     
26754     specialChars : [
26755            "&#169;",
26756           "&#174;",     
26757           "&#8482;",    
26758           "&#163;" ,    
26759          // "&#8212;",    
26760           "&#8230;",    
26761           "&#247;" ,    
26762         //  "&#225;" ,     ?? a acute?
26763            "&#8364;"    , //Euro
26764        //   "&#8220;"    ,
26765         //  "&#8221;"    ,
26766         //  "&#8226;"    ,
26767           "&#176;"  //   , // degrees
26768
26769          // "&#233;"     , // e ecute
26770          // "&#250;"     , // u ecute?
26771     ],
26772     
26773     specialElements : [
26774         {
26775             text: "Insert Table",
26776             xtype: 'MenuItem',
26777             xns : Roo.Menu,
26778             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26779                 
26780         },
26781         {    
26782             text: "Insert Image",
26783             xtype: 'MenuItem',
26784             xns : Roo.Menu,
26785             ihtml : '<img src="about:blank"/>'
26786             
26787         }
26788         
26789          
26790     ],
26791     
26792     
26793     inputElements : [ 
26794             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26795             "input:submit", "input:button", "select", "textarea", "label" ],
26796     formats : [
26797         ["p"] ,  
26798         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26799         ["pre"],[ "code"], 
26800         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26801         ['div'],['span']
26802     ],
26803     
26804     cleanStyles : [
26805         "font-size"
26806     ],
26807      /**
26808      * @cfg {String} defaultFont default font to use.
26809      */
26810     defaultFont: 'tahoma',
26811    
26812     fontSelect : false,
26813     
26814     
26815     formatCombo : false,
26816     
26817     init : function(editor)
26818     {
26819         this.editor = editor;
26820         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26821         var editorcore = this.editorcore;
26822         
26823         var _t = this;
26824         
26825         var fid = editorcore.frameId;
26826         var etb = this;
26827         function btn(id, toggle, handler){
26828             var xid = fid + '-'+ id ;
26829             return {
26830                 id : xid,
26831                 cmd : id,
26832                 cls : 'x-btn-icon x-edit-'+id,
26833                 enableToggle:toggle !== false,
26834                 scope: _t, // was editor...
26835                 handler:handler||_t.relayBtnCmd,
26836                 clickEvent:'mousedown',
26837                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26838                 tabIndex:-1
26839             };
26840         }
26841         
26842         
26843         
26844         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26845         this.tb = tb;
26846          // stop form submits
26847         tb.el.on('click', function(e){
26848             e.preventDefault(); // what does this do?
26849         });
26850
26851         if(!this.disable.font) { // && !Roo.isSafari){
26852             /* why no safari for fonts 
26853             editor.fontSelect = tb.el.createChild({
26854                 tag:'select',
26855                 tabIndex: -1,
26856                 cls:'x-font-select',
26857                 html: this.createFontOptions()
26858             });
26859             
26860             editor.fontSelect.on('change', function(){
26861                 var font = editor.fontSelect.dom.value;
26862                 editor.relayCmd('fontname', font);
26863                 editor.deferFocus();
26864             }, editor);
26865             
26866             tb.add(
26867                 editor.fontSelect.dom,
26868                 '-'
26869             );
26870             */
26871             
26872         };
26873         if(!this.disable.formats){
26874             this.formatCombo = new Roo.form.ComboBox({
26875                 store: new Roo.data.SimpleStore({
26876                     id : 'tag',
26877                     fields: ['tag'],
26878                     data : this.formats // from states.js
26879                 }),
26880                 blockFocus : true,
26881                 name : '',
26882                 //autoCreate : {tag: "div",  size: "20"},
26883                 displayField:'tag',
26884                 typeAhead: false,
26885                 mode: 'local',
26886                 editable : false,
26887                 triggerAction: 'all',
26888                 emptyText:'Add tag',
26889                 selectOnFocus:true,
26890                 width:135,
26891                 listeners : {
26892                     'select': function(c, r, i) {
26893                         editorcore.insertTag(r.get('tag'));
26894                         editor.focus();
26895                     }
26896                 }
26897
26898             });
26899             tb.addField(this.formatCombo);
26900             
26901         }
26902         
26903         if(!this.disable.format){
26904             tb.add(
26905                 btn('bold'),
26906                 btn('italic'),
26907                 btn('underline')
26908             );
26909         };
26910         if(!this.disable.fontSize){
26911             tb.add(
26912                 '-',
26913                 
26914                 
26915                 btn('increasefontsize', false, editorcore.adjustFont),
26916                 btn('decreasefontsize', false, editorcore.adjustFont)
26917             );
26918         };
26919         
26920         
26921         if(!this.disable.colors){
26922             tb.add(
26923                 '-', {
26924                     id:editorcore.frameId +'-forecolor',
26925                     cls:'x-btn-icon x-edit-forecolor',
26926                     clickEvent:'mousedown',
26927                     tooltip: this.buttonTips['forecolor'] || undefined,
26928                     tabIndex:-1,
26929                     menu : new Roo.menu.ColorMenu({
26930                         allowReselect: true,
26931                         focus: Roo.emptyFn,
26932                         value:'000000',
26933                         plain:true,
26934                         selectHandler: function(cp, color){
26935                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26936                             editor.deferFocus();
26937                         },
26938                         scope: editorcore,
26939                         clickEvent:'mousedown'
26940                     })
26941                 }, {
26942                     id:editorcore.frameId +'backcolor',
26943                     cls:'x-btn-icon x-edit-backcolor',
26944                     clickEvent:'mousedown',
26945                     tooltip: this.buttonTips['backcolor'] || undefined,
26946                     tabIndex:-1,
26947                     menu : new Roo.menu.ColorMenu({
26948                         focus: Roo.emptyFn,
26949                         value:'FFFFFF',
26950                         plain:true,
26951                         allowReselect: true,
26952                         selectHandler: function(cp, color){
26953                             if(Roo.isGecko){
26954                                 editorcore.execCmd('useCSS', false);
26955                                 editorcore.execCmd('hilitecolor', color);
26956                                 editorcore.execCmd('useCSS', true);
26957                                 editor.deferFocus();
26958                             }else{
26959                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26960                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26961                                 editor.deferFocus();
26962                             }
26963                         },
26964                         scope:editorcore,
26965                         clickEvent:'mousedown'
26966                     })
26967                 }
26968             );
26969         };
26970         // now add all the items...
26971         
26972
26973         if(!this.disable.alignments){
26974             tb.add(
26975                 '-',
26976                 btn('justifyleft'),
26977                 btn('justifycenter'),
26978                 btn('justifyright')
26979             );
26980         };
26981
26982         //if(!Roo.isSafari){
26983             if(!this.disable.links){
26984                 tb.add(
26985                     '-',
26986                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26987                 );
26988             };
26989
26990             if(!this.disable.lists){
26991                 tb.add(
26992                     '-',
26993                     btn('insertorderedlist'),
26994                     btn('insertunorderedlist')
26995                 );
26996             }
26997             if(!this.disable.sourceEdit){
26998                 tb.add(
26999                     '-',
27000                     btn('sourceedit', true, function(btn){
27001                         Roo.log(this);
27002                         this.toggleSourceEdit(btn.pressed);
27003                     })
27004                 );
27005             }
27006         //}
27007         
27008         var smenu = { };
27009         // special menu.. - needs to be tidied up..
27010         if (!this.disable.special) {
27011             smenu = {
27012                 text: "&#169;",
27013                 cls: 'x-edit-none',
27014                 
27015                 menu : {
27016                     items : []
27017                 }
27018             };
27019             for (var i =0; i < this.specialChars.length; i++) {
27020                 smenu.menu.items.push({
27021                     
27022                     html: this.specialChars[i],
27023                     handler: function(a,b) {
27024                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27025                         //editor.insertAtCursor(a.html);
27026                         
27027                     },
27028                     tabIndex:-1
27029                 });
27030             }
27031             
27032             
27033             tb.add(smenu);
27034             
27035             
27036         }
27037         
27038         var cmenu = { };
27039         if (!this.disable.cleanStyles) {
27040             cmenu = {
27041                 cls: 'x-btn-icon x-btn-clear',
27042                 
27043                 menu : {
27044                     items : []
27045                 }
27046             };
27047             for (var i =0; i < this.cleanStyles.length; i++) {
27048                 cmenu.menu.items.push({
27049                     actiontype : this.cleanStyles[i],
27050                     html: 'Remove ' + this.cleanStyles[i],
27051                     handler: function(a,b) {
27052                         Roo.log(a);
27053                         Roo.log(b);
27054                         var c = Roo.get(editorcore.doc.body);
27055                         c.select('[style]').each(function(s) {
27056                             s.dom.style.removeProperty(a.actiontype);
27057                         });
27058                         editorcore.syncValue();
27059                     },
27060                     tabIndex:-1
27061                 });
27062             }
27063             cmenu.menu.items.push({
27064                 actiontype : 'word',
27065                 html: 'Remove MS Word Formating',
27066                 handler: function(a,b) {
27067                     editorcore.cleanWord();
27068                     editorcore.syncValue();
27069                 },
27070                 tabIndex:-1
27071             });
27072             
27073             cmenu.menu.items.push({
27074                 actiontype : 'all',
27075                 html: 'Remove All Styles',
27076                 handler: function(a,b) {
27077                     
27078                     var c = Roo.get(editorcore.doc.body);
27079                     c.select('[style]').each(function(s) {
27080                         s.dom.removeAttribute('style');
27081                     });
27082                     editorcore.syncValue();
27083                 },
27084                 tabIndex:-1
27085             });
27086              cmenu.menu.items.push({
27087                 actiontype : 'word',
27088                 html: 'Tidy HTML Source',
27089                 handler: function(a,b) {
27090                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27091                     editorcore.syncValue();
27092                 },
27093                 tabIndex:-1
27094             });
27095             
27096             
27097             tb.add(cmenu);
27098         }
27099          
27100         if (!this.disable.specialElements) {
27101             var semenu = {
27102                 text: "Other;",
27103                 cls: 'x-edit-none',
27104                 menu : {
27105                     items : []
27106                 }
27107             };
27108             for (var i =0; i < this.specialElements.length; i++) {
27109                 semenu.menu.items.push(
27110                     Roo.apply({ 
27111                         handler: function(a,b) {
27112                             editor.insertAtCursor(this.ihtml);
27113                         }
27114                     }, this.specialElements[i])
27115                 );
27116                     
27117             }
27118             
27119             tb.add(semenu);
27120             
27121             
27122         }
27123          
27124         
27125         if (this.btns) {
27126             for(var i =0; i< this.btns.length;i++) {
27127                 var b = Roo.factory(this.btns[i],Roo.form);
27128                 b.cls =  'x-edit-none';
27129                 b.scope = editorcore;
27130                 tb.add(b);
27131             }
27132         
27133         }
27134         
27135         
27136         
27137         // disable everything...
27138         
27139         this.tb.items.each(function(item){
27140            if(item.id != editorcore.frameId+ '-sourceedit'){
27141                 item.disable();
27142             }
27143         });
27144         this.rendered = true;
27145         
27146         // the all the btns;
27147         editor.on('editorevent', this.updateToolbar, this);
27148         // other toolbars need to implement this..
27149         //editor.on('editmodechange', this.updateToolbar, this);
27150     },
27151     
27152     
27153     relayBtnCmd : function(btn) {
27154         this.editorcore.relayCmd(btn.cmd);
27155     },
27156     // private used internally
27157     createLink : function(){
27158         Roo.log("create link?");
27159         var url = prompt(this.createLinkText, this.defaultLinkValue);
27160         if(url && url != 'http:/'+'/'){
27161             this.editorcore.relayCmd('createlink', url);
27162         }
27163     },
27164
27165     
27166     /**
27167      * Protected method that will not generally be called directly. It triggers
27168      * a toolbar update by reading the markup state of the current selection in the editor.
27169      */
27170     updateToolbar: function(){
27171
27172         if(!this.editorcore.activated){
27173             this.editor.onFirstFocus();
27174             return;
27175         }
27176
27177         var btns = this.tb.items.map, 
27178             doc = this.editorcore.doc,
27179             frameId = this.editorcore.frameId;
27180
27181         if(!this.disable.font && !Roo.isSafari){
27182             /*
27183             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27184             if(name != this.fontSelect.dom.value){
27185                 this.fontSelect.dom.value = name;
27186             }
27187             */
27188         }
27189         if(!this.disable.format){
27190             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27191             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27192             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27193         }
27194         if(!this.disable.alignments){
27195             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27196             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27197             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27198         }
27199         if(!Roo.isSafari && !this.disable.lists){
27200             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27201             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27202         }
27203         
27204         var ans = this.editorcore.getAllAncestors();
27205         if (this.formatCombo) {
27206             
27207             
27208             var store = this.formatCombo.store;
27209             this.formatCombo.setValue("");
27210             for (var i =0; i < ans.length;i++) {
27211                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27212                     // select it..
27213                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27214                     break;
27215                 }
27216             }
27217         }
27218         
27219         
27220         
27221         // hides menus... - so this cant be on a menu...
27222         Roo.menu.MenuMgr.hideAll();
27223
27224         //this.editorsyncValue();
27225     },
27226    
27227     
27228     createFontOptions : function(){
27229         var buf = [], fs = this.fontFamilies, ff, lc;
27230         
27231         
27232         
27233         for(var i = 0, len = fs.length; i< len; i++){
27234             ff = fs[i];
27235             lc = ff.toLowerCase();
27236             buf.push(
27237                 '<option value="',lc,'" style="font-family:',ff,';"',
27238                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27239                     ff,
27240                 '</option>'
27241             );
27242         }
27243         return buf.join('');
27244     },
27245     
27246     toggleSourceEdit : function(sourceEditMode){
27247         
27248         Roo.log("toolbar toogle");
27249         if(sourceEditMode === undefined){
27250             sourceEditMode = !this.sourceEditMode;
27251         }
27252         this.sourceEditMode = sourceEditMode === true;
27253         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27254         // just toggle the button?
27255         if(btn.pressed !== this.sourceEditMode){
27256             btn.toggle(this.sourceEditMode);
27257             return;
27258         }
27259         
27260         if(sourceEditMode){
27261             Roo.log("disabling buttons");
27262             this.tb.items.each(function(item){
27263                 if(item.cmd != 'sourceedit'){
27264                     item.disable();
27265                 }
27266             });
27267           
27268         }else{
27269             Roo.log("enabling buttons");
27270             if(this.editorcore.initialized){
27271                 this.tb.items.each(function(item){
27272                     item.enable();
27273                 });
27274             }
27275             
27276         }
27277         Roo.log("calling toggole on editor");
27278         // tell the editor that it's been pressed..
27279         this.editor.toggleSourceEdit(sourceEditMode);
27280        
27281     },
27282      /**
27283      * Object collection of toolbar tooltips for the buttons in the editor. The key
27284      * is the command id associated with that button and the value is a valid QuickTips object.
27285      * For example:
27286 <pre><code>
27287 {
27288     bold : {
27289         title: 'Bold (Ctrl+B)',
27290         text: 'Make the selected text bold.',
27291         cls: 'x-html-editor-tip'
27292     },
27293     italic : {
27294         title: 'Italic (Ctrl+I)',
27295         text: 'Make the selected text italic.',
27296         cls: 'x-html-editor-tip'
27297     },
27298     ...
27299 </code></pre>
27300     * @type Object
27301      */
27302     buttonTips : {
27303         bold : {
27304             title: 'Bold (Ctrl+B)',
27305             text: 'Make the selected text bold.',
27306             cls: 'x-html-editor-tip'
27307         },
27308         italic : {
27309             title: 'Italic (Ctrl+I)',
27310             text: 'Make the selected text italic.',
27311             cls: 'x-html-editor-tip'
27312         },
27313         underline : {
27314             title: 'Underline (Ctrl+U)',
27315             text: 'Underline the selected text.',
27316             cls: 'x-html-editor-tip'
27317         },
27318         increasefontsize : {
27319             title: 'Grow Text',
27320             text: 'Increase the font size.',
27321             cls: 'x-html-editor-tip'
27322         },
27323         decreasefontsize : {
27324             title: 'Shrink Text',
27325             text: 'Decrease the font size.',
27326             cls: 'x-html-editor-tip'
27327         },
27328         backcolor : {
27329             title: 'Text Highlight Color',
27330             text: 'Change the background color of the selected text.',
27331             cls: 'x-html-editor-tip'
27332         },
27333         forecolor : {
27334             title: 'Font Color',
27335             text: 'Change the color of the selected text.',
27336             cls: 'x-html-editor-tip'
27337         },
27338         justifyleft : {
27339             title: 'Align Text Left',
27340             text: 'Align text to the left.',
27341             cls: 'x-html-editor-tip'
27342         },
27343         justifycenter : {
27344             title: 'Center Text',
27345             text: 'Center text in the editor.',
27346             cls: 'x-html-editor-tip'
27347         },
27348         justifyright : {
27349             title: 'Align Text Right',
27350             text: 'Align text to the right.',
27351             cls: 'x-html-editor-tip'
27352         },
27353         insertunorderedlist : {
27354             title: 'Bullet List',
27355             text: 'Start a bulleted list.',
27356             cls: 'x-html-editor-tip'
27357         },
27358         insertorderedlist : {
27359             title: 'Numbered List',
27360             text: 'Start a numbered list.',
27361             cls: 'x-html-editor-tip'
27362         },
27363         createlink : {
27364             title: 'Hyperlink',
27365             text: 'Make the selected text a hyperlink.',
27366             cls: 'x-html-editor-tip'
27367         },
27368         sourceedit : {
27369             title: 'Source Edit',
27370             text: 'Switch to source editing mode.',
27371             cls: 'x-html-editor-tip'
27372         }
27373     },
27374     // private
27375     onDestroy : function(){
27376         if(this.rendered){
27377             
27378             this.tb.items.each(function(item){
27379                 if(item.menu){
27380                     item.menu.removeAll();
27381                     if(item.menu.el){
27382                         item.menu.el.destroy();
27383                     }
27384                 }
27385                 item.destroy();
27386             });
27387              
27388         }
27389     },
27390     onFirstFocus: function() {
27391         this.tb.items.each(function(item){
27392            item.enable();
27393         });
27394     }
27395 });
27396
27397
27398
27399
27400 // <script type="text/javascript">
27401 /*
27402  * Based on
27403  * Ext JS Library 1.1.1
27404  * Copyright(c) 2006-2007, Ext JS, LLC.
27405  *  
27406  
27407  */
27408
27409  
27410 /**
27411  * @class Roo.form.HtmlEditor.ToolbarContext
27412  * Context Toolbar
27413  * 
27414  * Usage:
27415  *
27416  new Roo.form.HtmlEditor({
27417     ....
27418     toolbars : [
27419         { xtype: 'ToolbarStandard', styles : {} }
27420         { xtype: 'ToolbarContext', disable : {} }
27421     ]
27422 })
27423
27424      
27425  * 
27426  * @config : {Object} disable List of elements to disable.. (not done yet.)
27427  * @config : {Object} styles  Map of styles available.
27428  * 
27429  */
27430
27431 Roo.form.HtmlEditor.ToolbarContext = function(config)
27432 {
27433     
27434     Roo.apply(this, config);
27435     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27436     // dont call parent... till later.
27437     this.styles = this.styles || {};
27438 }
27439
27440  
27441
27442 Roo.form.HtmlEditor.ToolbarContext.types = {
27443     'IMG' : {
27444         width : {
27445             title: "Width",
27446             width: 40
27447         },
27448         height:  {
27449             title: "Height",
27450             width: 40
27451         },
27452         align: {
27453             title: "Align",
27454             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27455             width : 80
27456             
27457         },
27458         border: {
27459             title: "Border",
27460             width: 40
27461         },
27462         alt: {
27463             title: "Alt",
27464             width: 120
27465         },
27466         src : {
27467             title: "Src",
27468             width: 220
27469         }
27470         
27471     },
27472     'A' : {
27473         name : {
27474             title: "Name",
27475             width: 50
27476         },
27477         target:  {
27478             title: "Target",
27479             width: 120
27480         },
27481         href:  {
27482             title: "Href",
27483             width: 220
27484         } // border?
27485         
27486     },
27487     'TABLE' : {
27488         rows : {
27489             title: "Rows",
27490             width: 20
27491         },
27492         cols : {
27493             title: "Cols",
27494             width: 20
27495         },
27496         width : {
27497             title: "Width",
27498             width: 40
27499         },
27500         height : {
27501             title: "Height",
27502             width: 40
27503         },
27504         border : {
27505             title: "Border",
27506             width: 20
27507         }
27508     },
27509     'TD' : {
27510         width : {
27511             title: "Width",
27512             width: 40
27513         },
27514         height : {
27515             title: "Height",
27516             width: 40
27517         },   
27518         align: {
27519             title: "Align",
27520             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27521             width: 80
27522         },
27523         valign: {
27524             title: "Valign",
27525             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27526             width: 80
27527         },
27528         colspan: {
27529             title: "Colspan",
27530             width: 20
27531             
27532         },
27533          'font-family'  : {
27534             title : "Font",
27535             style : 'fontFamily',
27536             displayField: 'display',
27537             optname : 'font-family',
27538             width: 140
27539         }
27540     },
27541     'INPUT' : {
27542         name : {
27543             title: "name",
27544             width: 120
27545         },
27546         value : {
27547             title: "Value",
27548             width: 120
27549         },
27550         width : {
27551             title: "Width",
27552             width: 40
27553         }
27554     },
27555     'LABEL' : {
27556         'for' : {
27557             title: "For",
27558             width: 120
27559         }
27560     },
27561     'TEXTAREA' : {
27562           name : {
27563             title: "name",
27564             width: 120
27565         },
27566         rows : {
27567             title: "Rows",
27568             width: 20
27569         },
27570         cols : {
27571             title: "Cols",
27572             width: 20
27573         }
27574     },
27575     'SELECT' : {
27576         name : {
27577             title: "name",
27578             width: 120
27579         },
27580         selectoptions : {
27581             title: "Options",
27582             width: 200
27583         }
27584     },
27585     
27586     // should we really allow this??
27587     // should this just be 
27588     'BODY' : {
27589         title : {
27590             title: "Title",
27591             width: 200,
27592             disabled : true
27593         }
27594     },
27595     'SPAN' : {
27596         'font-family'  : {
27597             title : "Font",
27598             style : 'fontFamily',
27599             displayField: 'display',
27600             optname : 'font-family',
27601             width: 140
27602         }
27603     },
27604     'DIV' : {
27605         'font-family'  : {
27606             title : "Font",
27607             style : 'fontFamily',
27608             displayField: 'display',
27609             optname : 'font-family',
27610             width: 140
27611         }
27612     },
27613      'P' : {
27614         'font-family'  : {
27615             title : "Font",
27616             style : 'fontFamily',
27617             displayField: 'display',
27618             optname : 'font-family',
27619             width: 140
27620         }
27621     },
27622     
27623     '*' : {
27624         // empty..
27625     }
27626
27627 };
27628
27629 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27630 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27631
27632 Roo.form.HtmlEditor.ToolbarContext.options = {
27633         'font-family'  : [ 
27634                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27635                 [ 'Courier New', 'Courier New'],
27636                 [ 'Tahoma', 'Tahoma'],
27637                 [ 'Times New Roman,serif', 'Times'],
27638                 [ 'Verdana','Verdana' ]
27639         ]
27640 };
27641
27642 // fixme - these need to be configurable..
27643  
27644
27645 Roo.form.HtmlEditor.ToolbarContext.types
27646
27647
27648 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27649     
27650     tb: false,
27651     
27652     rendered: false,
27653     
27654     editor : false,
27655     editorcore : false,
27656     /**
27657      * @cfg {Object} disable  List of toolbar elements to disable
27658          
27659      */
27660     disable : false,
27661     /**
27662      * @cfg {Object} styles List of styles 
27663      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27664      *
27665      * These must be defined in the page, so they get rendered correctly..
27666      * .headline { }
27667      * TD.underline { }
27668      * 
27669      */
27670     styles : false,
27671     
27672     options: false,
27673     
27674     toolbars : false,
27675     
27676     init : function(editor)
27677     {
27678         this.editor = editor;
27679         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27680         var editorcore = this.editorcore;
27681         
27682         var fid = editorcore.frameId;
27683         var etb = this;
27684         function btn(id, toggle, handler){
27685             var xid = fid + '-'+ id ;
27686             return {
27687                 id : xid,
27688                 cmd : id,
27689                 cls : 'x-btn-icon x-edit-'+id,
27690                 enableToggle:toggle !== false,
27691                 scope: editorcore, // was editor...
27692                 handler:handler||editorcore.relayBtnCmd,
27693                 clickEvent:'mousedown',
27694                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27695                 tabIndex:-1
27696             };
27697         }
27698         // create a new element.
27699         var wdiv = editor.wrap.createChild({
27700                 tag: 'div'
27701             }, editor.wrap.dom.firstChild.nextSibling, true);
27702         
27703         // can we do this more than once??
27704         
27705          // stop form submits
27706       
27707  
27708         // disable everything...
27709         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27710         this.toolbars = {};
27711            
27712         for (var i in  ty) {
27713           
27714             this.toolbars[i] = this.buildToolbar(ty[i],i);
27715         }
27716         this.tb = this.toolbars.BODY;
27717         this.tb.el.show();
27718         this.buildFooter();
27719         this.footer.show();
27720         editor.on('hide', function( ) { this.footer.hide() }, this);
27721         editor.on('show', function( ) { this.footer.show() }, this);
27722         
27723          
27724         this.rendered = true;
27725         
27726         // the all the btns;
27727         editor.on('editorevent', this.updateToolbar, this);
27728         // other toolbars need to implement this..
27729         //editor.on('editmodechange', this.updateToolbar, this);
27730     },
27731     
27732     
27733     
27734     /**
27735      * Protected method that will not generally be called directly. It triggers
27736      * a toolbar update by reading the markup state of the current selection in the editor.
27737      */
27738     updateToolbar: function(editor,ev,sel){
27739
27740         //Roo.log(ev);
27741         // capture mouse up - this is handy for selecting images..
27742         // perhaps should go somewhere else...
27743         if(!this.editorcore.activated){
27744              this.editor.onFirstFocus();
27745             return;
27746         }
27747         
27748         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27749         // selectNode - might want to handle IE?
27750         if (ev &&
27751             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27752             ev.target && ev.target.tagName == 'IMG') {
27753             // they have click on an image...
27754             // let's see if we can change the selection...
27755             sel = ev.target;
27756          
27757               var nodeRange = sel.ownerDocument.createRange();
27758             try {
27759                 nodeRange.selectNode(sel);
27760             } catch (e) {
27761                 nodeRange.selectNodeContents(sel);
27762             }
27763             //nodeRange.collapse(true);
27764             var s = this.editorcore.win.getSelection();
27765             s.removeAllRanges();
27766             s.addRange(nodeRange);
27767         }  
27768         
27769       
27770         var updateFooter = sel ? false : true;
27771         
27772         
27773         var ans = this.editorcore.getAllAncestors();
27774         
27775         // pick
27776         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27777         
27778         if (!sel) { 
27779             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27780             sel = sel ? sel : this.editorcore.doc.body;
27781             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27782             
27783         }
27784         // pick a menu that exists..
27785         var tn = sel.tagName.toUpperCase();
27786         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27787         
27788         tn = sel.tagName.toUpperCase();
27789         
27790         var lastSel = this.tb.selectedNode
27791         
27792         this.tb.selectedNode = sel;
27793         
27794         // if current menu does not match..
27795         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27796                 
27797             this.tb.el.hide();
27798             ///console.log("show: " + tn);
27799             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27800             this.tb.el.show();
27801             // update name
27802             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27803             
27804             
27805             // update attributes
27806             if (this.tb.fields) {
27807                 this.tb.fields.each(function(e) {
27808                     if (e.stylename) {
27809                         e.setValue(sel.style[e.stylename]);
27810                         return;
27811                     } 
27812                    e.setValue(sel.getAttribute(e.attrname));
27813                 });
27814             }
27815             
27816             var hasStyles = false;
27817             for(var i in this.styles) {
27818                 hasStyles = true;
27819                 break;
27820             }
27821             
27822             // update styles
27823             if (hasStyles) { 
27824                 var st = this.tb.fields.item(0);
27825                 
27826                 st.store.removeAll();
27827                
27828                 
27829                 var cn = sel.className.split(/\s+/);
27830                 
27831                 var avs = [];
27832                 if (this.styles['*']) {
27833                     
27834                     Roo.each(this.styles['*'], function(v) {
27835                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27836                     });
27837                 }
27838                 if (this.styles[tn]) { 
27839                     Roo.each(this.styles[tn], function(v) {
27840                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27841                     });
27842                 }
27843                 
27844                 st.store.loadData(avs);
27845                 st.collapse();
27846                 st.setValue(cn);
27847             }
27848             // flag our selected Node.
27849             this.tb.selectedNode = sel;
27850            
27851            
27852             Roo.menu.MenuMgr.hideAll();
27853
27854         }
27855         
27856         if (!updateFooter) {
27857             //this.footDisp.dom.innerHTML = ''; 
27858             return;
27859         }
27860         // update the footer
27861         //
27862         var html = '';
27863         
27864         this.footerEls = ans.reverse();
27865         Roo.each(this.footerEls, function(a,i) {
27866             if (!a) { return; }
27867             html += html.length ? ' &gt; '  :  '';
27868             
27869             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27870             
27871         });
27872        
27873         // 
27874         var sz = this.footDisp.up('td').getSize();
27875         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27876         this.footDisp.dom.style.marginLeft = '5px';
27877         
27878         this.footDisp.dom.style.overflow = 'hidden';
27879         
27880         this.footDisp.dom.innerHTML = html;
27881             
27882         //this.editorsyncValue();
27883     },
27884      
27885     
27886    
27887        
27888     // private
27889     onDestroy : function(){
27890         if(this.rendered){
27891             
27892             this.tb.items.each(function(item){
27893                 if(item.menu){
27894                     item.menu.removeAll();
27895                     if(item.menu.el){
27896                         item.menu.el.destroy();
27897                     }
27898                 }
27899                 item.destroy();
27900             });
27901              
27902         }
27903     },
27904     onFirstFocus: function() {
27905         // need to do this for all the toolbars..
27906         this.tb.items.each(function(item){
27907            item.enable();
27908         });
27909     },
27910     buildToolbar: function(tlist, nm)
27911     {
27912         var editor = this.editor;
27913         var editorcore = this.editorcore;
27914          // create a new element.
27915         var wdiv = editor.wrap.createChild({
27916                 tag: 'div'
27917             }, editor.wrap.dom.firstChild.nextSibling, true);
27918         
27919        
27920         var tb = new Roo.Toolbar(wdiv);
27921         // add the name..
27922         
27923         tb.add(nm+ ":&nbsp;");
27924         
27925         var styles = [];
27926         for(var i in this.styles) {
27927             styles.push(i);
27928         }
27929         
27930         // styles...
27931         if (styles && styles.length) {
27932             
27933             // this needs a multi-select checkbox...
27934             tb.addField( new Roo.form.ComboBox({
27935                 store: new Roo.data.SimpleStore({
27936                     id : 'val',
27937                     fields: ['val', 'selected'],
27938                     data : [] 
27939                 }),
27940                 name : '-roo-edit-className',
27941                 attrname : 'className',
27942                 displayField: 'val',
27943                 typeAhead: false,
27944                 mode: 'local',
27945                 editable : false,
27946                 triggerAction: 'all',
27947                 emptyText:'Select Style',
27948                 selectOnFocus:true,
27949                 width: 130,
27950                 listeners : {
27951                     'select': function(c, r, i) {
27952                         // initial support only for on class per el..
27953                         tb.selectedNode.className =  r ? r.get('val') : '';
27954                         editorcore.syncValue();
27955                     }
27956                 }
27957     
27958             }));
27959         }
27960         
27961         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27962         var tbops = tbc.options;
27963         
27964         for (var i in tlist) {
27965             
27966             var item = tlist[i];
27967             tb.add(item.title + ":&nbsp;");
27968             
27969             
27970             //optname == used so you can configure the options available..
27971             var opts = item.opts ? item.opts : false;
27972             if (item.optname) {
27973                 opts = tbops[item.optname];
27974            
27975             }
27976             
27977             if (opts) {
27978                 // opts == pulldown..
27979                 tb.addField( new Roo.form.ComboBox({
27980                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27981                         id : 'val',
27982                         fields: ['val', 'display'],
27983                         data : opts  
27984                     }),
27985                     name : '-roo-edit-' + i,
27986                     attrname : i,
27987                     stylename : item.style ? item.style : false,
27988                     displayField: item.displayField ? item.displayField : 'val',
27989                     valueField :  'val',
27990                     typeAhead: false,
27991                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27992                     editable : false,
27993                     triggerAction: 'all',
27994                     emptyText:'Select',
27995                     selectOnFocus:true,
27996                     width: item.width ? item.width  : 130,
27997                     listeners : {
27998                         'select': function(c, r, i) {
27999                             if (c.stylename) {
28000                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28001                                 return;
28002                             }
28003                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28004                         }
28005                     }
28006
28007                 }));
28008                 continue;
28009                     
28010                  
28011                 
28012                 tb.addField( new Roo.form.TextField({
28013                     name: i,
28014                     width: 100,
28015                     //allowBlank:false,
28016                     value: ''
28017                 }));
28018                 continue;
28019             }
28020             tb.addField( new Roo.form.TextField({
28021                 name: '-roo-edit-' + i,
28022                 attrname : i,
28023                 
28024                 width: item.width,
28025                 //allowBlank:true,
28026                 value: '',
28027                 listeners: {
28028                     'change' : function(f, nv, ov) {
28029                         tb.selectedNode.setAttribute(f.attrname, nv);
28030                     }
28031                 }
28032             }));
28033              
28034         }
28035         tb.addFill();
28036         var _this = this;
28037         tb.addButton( {
28038             text: 'Remove Tag',
28039     
28040             listeners : {
28041                 click : function ()
28042                 {
28043                     // remove
28044                     // undo does not work.
28045                      
28046                     var sn = tb.selectedNode;
28047                     
28048                     var pn = sn.parentNode;
28049                     
28050                     var stn =  sn.childNodes[0];
28051                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28052                     while (sn.childNodes.length) {
28053                         var node = sn.childNodes[0];
28054                         sn.removeChild(node);
28055                         //Roo.log(node);
28056                         pn.insertBefore(node, sn);
28057                         
28058                     }
28059                     pn.removeChild(sn);
28060                     var range = editorcore.createRange();
28061         
28062                     range.setStart(stn,0);
28063                     range.setEnd(en,0); //????
28064                     //range.selectNode(sel);
28065                     
28066                     
28067                     var selection = editorcore.getSelection();
28068                     selection.removeAllRanges();
28069                     selection.addRange(range);
28070                     
28071                     
28072                     
28073                     //_this.updateToolbar(null, null, pn);
28074                     _this.updateToolbar(null, null, null);
28075                     _this.footDisp.dom.innerHTML = ''; 
28076                 }
28077             }
28078             
28079                     
28080                 
28081             
28082         });
28083         
28084         
28085         tb.el.on('click', function(e){
28086             e.preventDefault(); // what does this do?
28087         });
28088         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28089         tb.el.hide();
28090         tb.name = nm;
28091         // dont need to disable them... as they will get hidden
28092         return tb;
28093          
28094         
28095     },
28096     buildFooter : function()
28097     {
28098         
28099         var fel = this.editor.wrap.createChild();
28100         this.footer = new Roo.Toolbar(fel);
28101         // toolbar has scrolly on left / right?
28102         var footDisp= new Roo.Toolbar.Fill();
28103         var _t = this;
28104         this.footer.add(
28105             {
28106                 text : '&lt;',
28107                 xtype: 'Button',
28108                 handler : function() {
28109                     _t.footDisp.scrollTo('left',0,true)
28110                 }
28111             }
28112         );
28113         this.footer.add( footDisp );
28114         this.footer.add( 
28115             {
28116                 text : '&gt;',
28117                 xtype: 'Button',
28118                 handler : function() {
28119                     // no animation..
28120                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28121                 }
28122             }
28123         );
28124         var fel = Roo.get(footDisp.el);
28125         fel.addClass('x-editor-context');
28126         this.footDispWrap = fel; 
28127         this.footDispWrap.overflow  = 'hidden';
28128         
28129         this.footDisp = fel.createChild();
28130         this.footDispWrap.on('click', this.onContextClick, this)
28131         
28132         
28133     },
28134     onContextClick : function (ev,dom)
28135     {
28136         ev.preventDefault();
28137         var  cn = dom.className;
28138         //Roo.log(cn);
28139         if (!cn.match(/x-ed-loc-/)) {
28140             return;
28141         }
28142         var n = cn.split('-').pop();
28143         var ans = this.footerEls;
28144         var sel = ans[n];
28145         
28146          // pick
28147         var range = this.editorcore.createRange();
28148         
28149         range.selectNodeContents(sel);
28150         //range.selectNode(sel);
28151         
28152         
28153         var selection = this.editorcore.getSelection();
28154         selection.removeAllRanges();
28155         selection.addRange(range);
28156         
28157         
28158         
28159         this.updateToolbar(null, null, sel);
28160         
28161         
28162     }
28163     
28164     
28165     
28166     
28167     
28168 });
28169
28170
28171
28172
28173
28174 /*
28175  * Based on:
28176  * Ext JS Library 1.1.1
28177  * Copyright(c) 2006-2007, Ext JS, LLC.
28178  *
28179  * Originally Released Under LGPL - original licence link has changed is not relivant.
28180  *
28181  * Fork - LGPL
28182  * <script type="text/javascript">
28183  */
28184  
28185 /**
28186  * @class Roo.form.BasicForm
28187  * @extends Roo.util.Observable
28188  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28189  * @constructor
28190  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28191  * @param {Object} config Configuration options
28192  */
28193 Roo.form.BasicForm = function(el, config){
28194     this.allItems = [];
28195     this.childForms = [];
28196     Roo.apply(this, config);
28197     /*
28198      * The Roo.form.Field items in this form.
28199      * @type MixedCollection
28200      */
28201      
28202      
28203     this.items = new Roo.util.MixedCollection(false, function(o){
28204         return o.id || (o.id = Roo.id());
28205     });
28206     this.addEvents({
28207         /**
28208          * @event beforeaction
28209          * Fires before any action is performed. Return false to cancel the action.
28210          * @param {Form} this
28211          * @param {Action} action The action to be performed
28212          */
28213         beforeaction: true,
28214         /**
28215          * @event actionfailed
28216          * Fires when an action fails.
28217          * @param {Form} this
28218          * @param {Action} action The action that failed
28219          */
28220         actionfailed : true,
28221         /**
28222          * @event actioncomplete
28223          * Fires when an action is completed.
28224          * @param {Form} this
28225          * @param {Action} action The action that completed
28226          */
28227         actioncomplete : true
28228     });
28229     if(el){
28230         this.initEl(el);
28231     }
28232     Roo.form.BasicForm.superclass.constructor.call(this);
28233 };
28234
28235 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28236     /**
28237      * @cfg {String} method
28238      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28239      */
28240     /**
28241      * @cfg {DataReader} reader
28242      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28243      * This is optional as there is built-in support for processing JSON.
28244      */
28245     /**
28246      * @cfg {DataReader} errorReader
28247      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28248      * This is completely optional as there is built-in support for processing JSON.
28249      */
28250     /**
28251      * @cfg {String} url
28252      * The URL to use for form actions if one isn't supplied in the action options.
28253      */
28254     /**
28255      * @cfg {Boolean} fileUpload
28256      * Set to true if this form is a file upload.
28257      */
28258      
28259     /**
28260      * @cfg {Object} baseParams
28261      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28262      */
28263      /**
28264      
28265     /**
28266      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28267      */
28268     timeout: 30,
28269
28270     // private
28271     activeAction : null,
28272
28273     /**
28274      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28275      * or setValues() data instead of when the form was first created.
28276      */
28277     trackResetOnLoad : false,
28278     
28279     
28280     /**
28281      * childForms - used for multi-tab forms
28282      * @type {Array}
28283      */
28284     childForms : false,
28285     
28286     /**
28287      * allItems - full list of fields.
28288      * @type {Array}
28289      */
28290     allItems : false,
28291     
28292     /**
28293      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28294      * element by passing it or its id or mask the form itself by passing in true.
28295      * @type Mixed
28296      */
28297     waitMsgTarget : false,
28298
28299     // private
28300     initEl : function(el){
28301         this.el = Roo.get(el);
28302         this.id = this.el.id || Roo.id();
28303         this.el.on('submit', this.onSubmit, this);
28304         this.el.addClass('x-form');
28305     },
28306
28307     // private
28308     onSubmit : function(e){
28309         e.stopEvent();
28310     },
28311
28312     /**
28313      * Returns true if client-side validation on the form is successful.
28314      * @return Boolean
28315      */
28316     isValid : function(){
28317         var valid = true;
28318         this.items.each(function(f){
28319            if(!f.validate()){
28320                valid = false;
28321            }
28322         });
28323         return valid;
28324     },
28325
28326     /**
28327      * Returns true if any fields in this form have changed since their original load.
28328      * @return Boolean
28329      */
28330     isDirty : function(){
28331         var dirty = false;
28332         this.items.each(function(f){
28333            if(f.isDirty()){
28334                dirty = true;
28335                return false;
28336            }
28337         });
28338         return dirty;
28339     },
28340
28341     /**
28342      * Performs a predefined action (submit or load) or custom actions you define on this form.
28343      * @param {String} actionName The name of the action type
28344      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28345      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28346      * accept other config options):
28347      * <pre>
28348 Property          Type             Description
28349 ----------------  ---------------  ----------------------------------------------------------------------------------
28350 url               String           The url for the action (defaults to the form's url)
28351 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28352 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28353 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28354                                    validate the form on the client (defaults to false)
28355      * </pre>
28356      * @return {BasicForm} this
28357      */
28358     doAction : function(action, options){
28359         if(typeof action == 'string'){
28360             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28361         }
28362         if(this.fireEvent('beforeaction', this, action) !== false){
28363             this.beforeAction(action);
28364             action.run.defer(100, action);
28365         }
28366         return this;
28367     },
28368
28369     /**
28370      * Shortcut to do a submit action.
28371      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28372      * @return {BasicForm} this
28373      */
28374     submit : function(options){
28375         this.doAction('submit', options);
28376         return this;
28377     },
28378
28379     /**
28380      * Shortcut to do a load action.
28381      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28382      * @return {BasicForm} this
28383      */
28384     load : function(options){
28385         this.doAction('load', options);
28386         return this;
28387     },
28388
28389     /**
28390      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28391      * @param {Record} record The record to edit
28392      * @return {BasicForm} this
28393      */
28394     updateRecord : function(record){
28395         record.beginEdit();
28396         var fs = record.fields;
28397         fs.each(function(f){
28398             var field = this.findField(f.name);
28399             if(field){
28400                 record.set(f.name, field.getValue());
28401             }
28402         }, this);
28403         record.endEdit();
28404         return this;
28405     },
28406
28407     /**
28408      * Loads an Roo.data.Record into this form.
28409      * @param {Record} record The record to load
28410      * @return {BasicForm} this
28411      */
28412     loadRecord : function(record){
28413         this.setValues(record.data);
28414         return this;
28415     },
28416
28417     // private
28418     beforeAction : function(action){
28419         var o = action.options;
28420         
28421        
28422         if(this.waitMsgTarget === true){
28423             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28424         }else if(this.waitMsgTarget){
28425             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28426             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28427         }else {
28428             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28429         }
28430          
28431     },
28432
28433     // private
28434     afterAction : function(action, success){
28435         this.activeAction = null;
28436         var o = action.options;
28437         
28438         if(this.waitMsgTarget === true){
28439             this.el.unmask();
28440         }else if(this.waitMsgTarget){
28441             this.waitMsgTarget.unmask();
28442         }else{
28443             Roo.MessageBox.updateProgress(1);
28444             Roo.MessageBox.hide();
28445         }
28446          
28447         if(success){
28448             if(o.reset){
28449                 this.reset();
28450             }
28451             Roo.callback(o.success, o.scope, [this, action]);
28452             this.fireEvent('actioncomplete', this, action);
28453             
28454         }else{
28455             
28456             // failure condition..
28457             // we have a scenario where updates need confirming.
28458             // eg. if a locking scenario exists..
28459             // we look for { errors : { needs_confirm : true }} in the response.
28460             if (
28461                 (typeof(action.result) != 'undefined')  &&
28462                 (typeof(action.result.errors) != 'undefined')  &&
28463                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28464            ){
28465                 var _t = this;
28466                 Roo.MessageBox.confirm(
28467                     "Change requires confirmation",
28468                     action.result.errorMsg,
28469                     function(r) {
28470                         if (r != 'yes') {
28471                             return;
28472                         }
28473                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28474                     }
28475                     
28476                 );
28477                 
28478                 
28479                 
28480                 return;
28481             }
28482             
28483             Roo.callback(o.failure, o.scope, [this, action]);
28484             // show an error message if no failed handler is set..
28485             if (!this.hasListener('actionfailed')) {
28486                 Roo.MessageBox.alert("Error",
28487                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28488                         action.result.errorMsg :
28489                         "Saving Failed, please check your entries or try again"
28490                 );
28491             }
28492             
28493             this.fireEvent('actionfailed', this, action);
28494         }
28495         
28496     },
28497
28498     /**
28499      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28500      * @param {String} id The value to search for
28501      * @return Field
28502      */
28503     findField : function(id){
28504         var field = this.items.get(id);
28505         if(!field){
28506             this.items.each(function(f){
28507                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28508                     field = f;
28509                     return false;
28510                 }
28511             });
28512         }
28513         return field || null;
28514     },
28515
28516     /**
28517      * Add a secondary form to this one, 
28518      * Used to provide tabbed forms. One form is primary, with hidden values 
28519      * which mirror the elements from the other forms.
28520      * 
28521      * @param {Roo.form.Form} form to add.
28522      * 
28523      */
28524     addForm : function(form)
28525     {
28526        
28527         if (this.childForms.indexOf(form) > -1) {
28528             // already added..
28529             return;
28530         }
28531         this.childForms.push(form);
28532         var n = '';
28533         Roo.each(form.allItems, function (fe) {
28534             
28535             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28536             if (this.findField(n)) { // already added..
28537                 return;
28538             }
28539             var add = new Roo.form.Hidden({
28540                 name : n
28541             });
28542             add.render(this.el);
28543             
28544             this.add( add );
28545         }, this);
28546         
28547     },
28548     /**
28549      * Mark fields in this form invalid in bulk.
28550      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28551      * @return {BasicForm} this
28552      */
28553     markInvalid : function(errors){
28554         if(errors instanceof Array){
28555             for(var i = 0, len = errors.length; i < len; i++){
28556                 var fieldError = errors[i];
28557                 var f = this.findField(fieldError.id);
28558                 if(f){
28559                     f.markInvalid(fieldError.msg);
28560                 }
28561             }
28562         }else{
28563             var field, id;
28564             for(id in errors){
28565                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28566                     field.markInvalid(errors[id]);
28567                 }
28568             }
28569         }
28570         Roo.each(this.childForms || [], function (f) {
28571             f.markInvalid(errors);
28572         });
28573         
28574         return this;
28575     },
28576
28577     /**
28578      * Set values for fields in this form in bulk.
28579      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28580      * @return {BasicForm} this
28581      */
28582     setValues : function(values){
28583         if(values instanceof Array){ // array of objects
28584             for(var i = 0, len = values.length; i < len; i++){
28585                 var v = values[i];
28586                 var f = this.findField(v.id);
28587                 if(f){
28588                     f.setValue(v.value);
28589                     if(this.trackResetOnLoad){
28590                         f.originalValue = f.getValue();
28591                     }
28592                 }
28593             }
28594         }else{ // object hash
28595             var field, id;
28596             for(id in values){
28597                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28598                     
28599                     if (field.setFromData && 
28600                         field.valueField && 
28601                         field.displayField &&
28602                         // combos' with local stores can 
28603                         // be queried via setValue()
28604                         // to set their value..
28605                         (field.store && !field.store.isLocal)
28606                         ) {
28607                         // it's a combo
28608                         var sd = { };
28609                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28610                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28611                         field.setFromData(sd);
28612                         
28613                     } else {
28614                         field.setValue(values[id]);
28615                     }
28616                     
28617                     
28618                     if(this.trackResetOnLoad){
28619                         field.originalValue = field.getValue();
28620                     }
28621                 }
28622             }
28623         }
28624          
28625         Roo.each(this.childForms || [], function (f) {
28626             f.setValues(values);
28627         });
28628                 
28629         return this;
28630     },
28631
28632     /**
28633      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28634      * they are returned as an array.
28635      * @param {Boolean} asString
28636      * @return {Object}
28637      */
28638     getValues : function(asString){
28639         if (this.childForms) {
28640             // copy values from the child forms
28641             Roo.each(this.childForms, function (f) {
28642                 this.setValues(f.getValues());
28643             }, this);
28644         }
28645         
28646         
28647         
28648         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28649         if(asString === true){
28650             return fs;
28651         }
28652         return Roo.urlDecode(fs);
28653     },
28654     
28655     /**
28656      * Returns the fields in this form as an object with key/value pairs. 
28657      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28658      * @return {Object}
28659      */
28660     getFieldValues : function(with_hidden)
28661     {
28662         if (this.childForms) {
28663             // copy values from the child forms
28664             // should this call getFieldValues - probably not as we do not currently copy
28665             // hidden fields when we generate..
28666             Roo.each(this.childForms, function (f) {
28667                 this.setValues(f.getValues());
28668             }, this);
28669         }
28670         
28671         var ret = {};
28672         this.items.each(function(f){
28673             if (!f.getName()) {
28674                 return;
28675             }
28676             var v = f.getValue();
28677             if (f.inputType =='radio') {
28678                 if (typeof(ret[f.getName()]) == 'undefined') {
28679                     ret[f.getName()] = ''; // empty..
28680                 }
28681                 
28682                 if (!f.el.dom.checked) {
28683                     return;
28684                     
28685                 }
28686                 v = f.el.dom.value;
28687                 
28688             }
28689             
28690             // not sure if this supported any more..
28691             if ((typeof(v) == 'object') && f.getRawValue) {
28692                 v = f.getRawValue() ; // dates..
28693             }
28694             // combo boxes where name != hiddenName...
28695             if (f.name != f.getName()) {
28696                 ret[f.name] = f.getRawValue();
28697             }
28698             ret[f.getName()] = v;
28699         });
28700         
28701         return ret;
28702     },
28703
28704     /**
28705      * Clears all invalid messages in this form.
28706      * @return {BasicForm} this
28707      */
28708     clearInvalid : function(){
28709         this.items.each(function(f){
28710            f.clearInvalid();
28711         });
28712         
28713         Roo.each(this.childForms || [], function (f) {
28714             f.clearInvalid();
28715         });
28716         
28717         
28718         return this;
28719     },
28720
28721     /**
28722      * Resets this form.
28723      * @return {BasicForm} this
28724      */
28725     reset : function(){
28726         this.items.each(function(f){
28727             f.reset();
28728         });
28729         
28730         Roo.each(this.childForms || [], function (f) {
28731             f.reset();
28732         });
28733        
28734         
28735         return this;
28736     },
28737
28738     /**
28739      * Add Roo.form components to this form.
28740      * @param {Field} field1
28741      * @param {Field} field2 (optional)
28742      * @param {Field} etc (optional)
28743      * @return {BasicForm} this
28744      */
28745     add : function(){
28746         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28747         return this;
28748     },
28749
28750
28751     /**
28752      * Removes a field from the items collection (does NOT remove its markup).
28753      * @param {Field} field
28754      * @return {BasicForm} this
28755      */
28756     remove : function(field){
28757         this.items.remove(field);
28758         return this;
28759     },
28760
28761     /**
28762      * Looks at the fields in this form, checks them for an id attribute,
28763      * and calls applyTo on the existing dom element with that id.
28764      * @return {BasicForm} this
28765      */
28766     render : function(){
28767         this.items.each(function(f){
28768             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28769                 f.applyTo(f.id);
28770             }
28771         });
28772         return this;
28773     },
28774
28775     /**
28776      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28777      * @param {Object} values
28778      * @return {BasicForm} this
28779      */
28780     applyToFields : function(o){
28781         this.items.each(function(f){
28782            Roo.apply(f, o);
28783         });
28784         return this;
28785     },
28786
28787     /**
28788      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28789      * @param {Object} values
28790      * @return {BasicForm} this
28791      */
28792     applyIfToFields : function(o){
28793         this.items.each(function(f){
28794            Roo.applyIf(f, o);
28795         });
28796         return this;
28797     }
28798 });
28799
28800 // back compat
28801 Roo.BasicForm = Roo.form.BasicForm;/*
28802  * Based on:
28803  * Ext JS Library 1.1.1
28804  * Copyright(c) 2006-2007, Ext JS, LLC.
28805  *
28806  * Originally Released Under LGPL - original licence link has changed is not relivant.
28807  *
28808  * Fork - LGPL
28809  * <script type="text/javascript">
28810  */
28811
28812 /**
28813  * @class Roo.form.Form
28814  * @extends Roo.form.BasicForm
28815  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28816  * @constructor
28817  * @param {Object} config Configuration options
28818  */
28819 Roo.form.Form = function(config){
28820     var xitems =  [];
28821     if (config.items) {
28822         xitems = config.items;
28823         delete config.items;
28824     }
28825    
28826     
28827     Roo.form.Form.superclass.constructor.call(this, null, config);
28828     this.url = this.url || this.action;
28829     if(!this.root){
28830         this.root = new Roo.form.Layout(Roo.applyIf({
28831             id: Roo.id()
28832         }, config));
28833     }
28834     this.active = this.root;
28835     /**
28836      * Array of all the buttons that have been added to this form via {@link addButton}
28837      * @type Array
28838      */
28839     this.buttons = [];
28840     this.allItems = [];
28841     this.addEvents({
28842         /**
28843          * @event clientvalidation
28844          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28845          * @param {Form} this
28846          * @param {Boolean} valid true if the form has passed client-side validation
28847          */
28848         clientvalidation: true,
28849         /**
28850          * @event rendered
28851          * Fires when the form is rendered
28852          * @param {Roo.form.Form} form
28853          */
28854         rendered : true
28855     });
28856     
28857     if (this.progressUrl) {
28858             // push a hidden field onto the list of fields..
28859             this.addxtype( {
28860                     xns: Roo.form, 
28861                     xtype : 'Hidden', 
28862                     name : 'UPLOAD_IDENTIFIER' 
28863             });
28864         }
28865         
28866     
28867     Roo.each(xitems, this.addxtype, this);
28868     
28869     
28870     
28871 };
28872
28873 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28874     /**
28875      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28876      */
28877     /**
28878      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28879      */
28880     /**
28881      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28882      */
28883     buttonAlign:'center',
28884
28885     /**
28886      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28887      */
28888     minButtonWidth:75,
28889
28890     /**
28891      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28892      * This property cascades to child containers if not set.
28893      */
28894     labelAlign:'left',
28895
28896     /**
28897      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28898      * fires a looping event with that state. This is required to bind buttons to the valid
28899      * state using the config value formBind:true on the button.
28900      */
28901     monitorValid : false,
28902
28903     /**
28904      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28905      */
28906     monitorPoll : 200,
28907     
28908     /**
28909      * @cfg {String} progressUrl - Url to return progress data 
28910      */
28911     
28912     progressUrl : false,
28913   
28914     /**
28915      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28916      * fields are added and the column is closed. If no fields are passed the column remains open
28917      * until end() is called.
28918      * @param {Object} config The config to pass to the column
28919      * @param {Field} field1 (optional)
28920      * @param {Field} field2 (optional)
28921      * @param {Field} etc (optional)
28922      * @return Column The column container object
28923      */
28924     column : function(c){
28925         var col = new Roo.form.Column(c);
28926         this.start(col);
28927         if(arguments.length > 1){ // duplicate code required because of Opera
28928             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28929             this.end();
28930         }
28931         return col;
28932     },
28933
28934     /**
28935      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28936      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28937      * until end() is called.
28938      * @param {Object} config The config to pass to the fieldset
28939      * @param {Field} field1 (optional)
28940      * @param {Field} field2 (optional)
28941      * @param {Field} etc (optional)
28942      * @return FieldSet The fieldset container object
28943      */
28944     fieldset : function(c){
28945         var fs = new Roo.form.FieldSet(c);
28946         this.start(fs);
28947         if(arguments.length > 1){ // duplicate code required because of Opera
28948             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28949             this.end();
28950         }
28951         return fs;
28952     },
28953
28954     /**
28955      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28956      * fields are added and the container is closed. If no fields are passed the container remains open
28957      * until end() is called.
28958      * @param {Object} config The config to pass to the Layout
28959      * @param {Field} field1 (optional)
28960      * @param {Field} field2 (optional)
28961      * @param {Field} etc (optional)
28962      * @return Layout The container object
28963      */
28964     container : function(c){
28965         var l = new Roo.form.Layout(c);
28966         this.start(l);
28967         if(arguments.length > 1){ // duplicate code required because of Opera
28968             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28969             this.end();
28970         }
28971         return l;
28972     },
28973
28974     /**
28975      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28976      * @param {Object} container A Roo.form.Layout or subclass of Layout
28977      * @return {Form} this
28978      */
28979     start : function(c){
28980         // cascade label info
28981         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28982         this.active.stack.push(c);
28983         c.ownerCt = this.active;
28984         this.active = c;
28985         return this;
28986     },
28987
28988     /**
28989      * Closes the current open container
28990      * @return {Form} this
28991      */
28992     end : function(){
28993         if(this.active == this.root){
28994             return this;
28995         }
28996         this.active = this.active.ownerCt;
28997         return this;
28998     },
28999
29000     /**
29001      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29002      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29003      * as the label of the field.
29004      * @param {Field} field1
29005      * @param {Field} field2 (optional)
29006      * @param {Field} etc. (optional)
29007      * @return {Form} this
29008      */
29009     add : function(){
29010         this.active.stack.push.apply(this.active.stack, arguments);
29011         this.allItems.push.apply(this.allItems,arguments);
29012         var r = [];
29013         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29014             if(a[i].isFormField){
29015                 r.push(a[i]);
29016             }
29017         }
29018         if(r.length > 0){
29019             Roo.form.Form.superclass.add.apply(this, r);
29020         }
29021         return this;
29022     },
29023     
29024
29025     
29026     
29027     
29028      /**
29029      * Find any element that has been added to a form, using it's ID or name
29030      * This can include framesets, columns etc. along with regular fields..
29031      * @param {String} id - id or name to find.
29032      
29033      * @return {Element} e - or false if nothing found.
29034      */
29035     findbyId : function(id)
29036     {
29037         var ret = false;
29038         if (!id) {
29039             return ret;
29040         }
29041         Roo.each(this.allItems, function(f){
29042             if (f.id == id || f.name == id ){
29043                 ret = f;
29044                 return false;
29045             }
29046         });
29047         return ret;
29048     },
29049
29050     
29051     
29052     /**
29053      * Render this form into the passed container. This should only be called once!
29054      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29055      * @return {Form} this
29056      */
29057     render : function(ct)
29058     {
29059         
29060         
29061         
29062         ct = Roo.get(ct);
29063         var o = this.autoCreate || {
29064             tag: 'form',
29065             method : this.method || 'POST',
29066             id : this.id || Roo.id()
29067         };
29068         this.initEl(ct.createChild(o));
29069
29070         this.root.render(this.el);
29071         
29072        
29073              
29074         this.items.each(function(f){
29075             f.render('x-form-el-'+f.id);
29076         });
29077
29078         if(this.buttons.length > 0){
29079             // tables are required to maintain order and for correct IE layout
29080             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29081                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29082                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29083             }}, null, true);
29084             var tr = tb.getElementsByTagName('tr')[0];
29085             for(var i = 0, len = this.buttons.length; i < len; i++) {
29086                 var b = this.buttons[i];
29087                 var td = document.createElement('td');
29088                 td.className = 'x-form-btn-td';
29089                 b.render(tr.appendChild(td));
29090             }
29091         }
29092         if(this.monitorValid){ // initialize after render
29093             this.startMonitoring();
29094         }
29095         this.fireEvent('rendered', this);
29096         return this;
29097     },
29098
29099     /**
29100      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29101      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29102      * object or a valid Roo.DomHelper element config
29103      * @param {Function} handler The function called when the button is clicked
29104      * @param {Object} scope (optional) The scope of the handler function
29105      * @return {Roo.Button}
29106      */
29107     addButton : function(config, handler, scope){
29108         var bc = {
29109             handler: handler,
29110             scope: scope,
29111             minWidth: this.minButtonWidth,
29112             hideParent:true
29113         };
29114         if(typeof config == "string"){
29115             bc.text = config;
29116         }else{
29117             Roo.apply(bc, config);
29118         }
29119         var btn = new Roo.Button(null, bc);
29120         this.buttons.push(btn);
29121         return btn;
29122     },
29123
29124      /**
29125      * Adds a series of form elements (using the xtype property as the factory method.
29126      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29127      * @param {Object} config 
29128      */
29129     
29130     addxtype : function()
29131     {
29132         var ar = Array.prototype.slice.call(arguments, 0);
29133         var ret = false;
29134         for(var i = 0; i < ar.length; i++) {
29135             if (!ar[i]) {
29136                 continue; // skip -- if this happends something invalid got sent, we 
29137                 // should ignore it, as basically that interface element will not show up
29138                 // and that should be pretty obvious!!
29139             }
29140             
29141             if (Roo.form[ar[i].xtype]) {
29142                 ar[i].form = this;
29143                 var fe = Roo.factory(ar[i], Roo.form);
29144                 if (!ret) {
29145                     ret = fe;
29146                 }
29147                 fe.form = this;
29148                 if (fe.store) {
29149                     fe.store.form = this;
29150                 }
29151                 if (fe.isLayout) {  
29152                          
29153                     this.start(fe);
29154                     this.allItems.push(fe);
29155                     if (fe.items && fe.addxtype) {
29156                         fe.addxtype.apply(fe, fe.items);
29157                         delete fe.items;
29158                     }
29159                      this.end();
29160                     continue;
29161                 }
29162                 
29163                 
29164                  
29165                 this.add(fe);
29166               //  console.log('adding ' + ar[i].xtype);
29167             }
29168             if (ar[i].xtype == 'Button') {  
29169                 //console.log('adding button');
29170                 //console.log(ar[i]);
29171                 this.addButton(ar[i]);
29172                 this.allItems.push(fe);
29173                 continue;
29174             }
29175             
29176             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29177                 alert('end is not supported on xtype any more, use items');
29178             //    this.end();
29179             //    //console.log('adding end');
29180             }
29181             
29182         }
29183         return ret;
29184     },
29185     
29186     /**
29187      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29188      * option "monitorValid"
29189      */
29190     startMonitoring : function(){
29191         if(!this.bound){
29192             this.bound = true;
29193             Roo.TaskMgr.start({
29194                 run : this.bindHandler,
29195                 interval : this.monitorPoll || 200,
29196                 scope: this
29197             });
29198         }
29199     },
29200
29201     /**
29202      * Stops monitoring of the valid state of this form
29203      */
29204     stopMonitoring : function(){
29205         this.bound = false;
29206     },
29207
29208     // private
29209     bindHandler : function(){
29210         if(!this.bound){
29211             return false; // stops binding
29212         }
29213         var valid = true;
29214         this.items.each(function(f){
29215             if(!f.isValid(true)){
29216                 valid = false;
29217                 return false;
29218             }
29219         });
29220         for(var i = 0, len = this.buttons.length; i < len; i++){
29221             var btn = this.buttons[i];
29222             if(btn.formBind === true && btn.disabled === valid){
29223                 btn.setDisabled(!valid);
29224             }
29225         }
29226         this.fireEvent('clientvalidation', this, valid);
29227     }
29228     
29229     
29230     
29231     
29232     
29233     
29234     
29235     
29236 });
29237
29238
29239 // back compat
29240 Roo.Form = Roo.form.Form;
29241 /*
29242  * Based on:
29243  * Ext JS Library 1.1.1
29244  * Copyright(c) 2006-2007, Ext JS, LLC.
29245  *
29246  * Originally Released Under LGPL - original licence link has changed is not relivant.
29247  *
29248  * Fork - LGPL
29249  * <script type="text/javascript">
29250  */
29251
29252 // as we use this in bootstrap.
29253 Roo.namespace('Roo.form');
29254  /**
29255  * @class Roo.form.Action
29256  * Internal Class used to handle form actions
29257  * @constructor
29258  * @param {Roo.form.BasicForm} el The form element or its id
29259  * @param {Object} config Configuration options
29260  */
29261
29262  
29263  
29264 // define the action interface
29265 Roo.form.Action = function(form, options){
29266     this.form = form;
29267     this.options = options || {};
29268 };
29269 /**
29270  * Client Validation Failed
29271  * @const 
29272  */
29273 Roo.form.Action.CLIENT_INVALID = 'client';
29274 /**
29275  * Server Validation Failed
29276  * @const 
29277  */
29278 Roo.form.Action.SERVER_INVALID = 'server';
29279  /**
29280  * Connect to Server Failed
29281  * @const 
29282  */
29283 Roo.form.Action.CONNECT_FAILURE = 'connect';
29284 /**
29285  * Reading Data from Server Failed
29286  * @const 
29287  */
29288 Roo.form.Action.LOAD_FAILURE = 'load';
29289
29290 Roo.form.Action.prototype = {
29291     type : 'default',
29292     failureType : undefined,
29293     response : undefined,
29294     result : undefined,
29295
29296     // interface method
29297     run : function(options){
29298
29299     },
29300
29301     // interface method
29302     success : function(response){
29303
29304     },
29305
29306     // interface method
29307     handleResponse : function(response){
29308
29309     },
29310
29311     // default connection failure
29312     failure : function(response){
29313         
29314         this.response = response;
29315         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29316         this.form.afterAction(this, false);
29317     },
29318
29319     processResponse : function(response){
29320         this.response = response;
29321         if(!response.responseText){
29322             return true;
29323         }
29324         this.result = this.handleResponse(response);
29325         return this.result;
29326     },
29327
29328     // utility functions used internally
29329     getUrl : function(appendParams){
29330         var url = this.options.url || this.form.url || this.form.el.dom.action;
29331         if(appendParams){
29332             var p = this.getParams();
29333             if(p){
29334                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29335             }
29336         }
29337         return url;
29338     },
29339
29340     getMethod : function(){
29341         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29342     },
29343
29344     getParams : function(){
29345         var bp = this.form.baseParams;
29346         var p = this.options.params;
29347         if(p){
29348             if(typeof p == "object"){
29349                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29350             }else if(typeof p == 'string' && bp){
29351                 p += '&' + Roo.urlEncode(bp);
29352             }
29353         }else if(bp){
29354             p = Roo.urlEncode(bp);
29355         }
29356         return p;
29357     },
29358
29359     createCallback : function(){
29360         return {
29361             success: this.success,
29362             failure: this.failure,
29363             scope: this,
29364             timeout: (this.form.timeout*1000),
29365             upload: this.form.fileUpload ? this.success : undefined
29366         };
29367     }
29368 };
29369
29370 Roo.form.Action.Submit = function(form, options){
29371     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29372 };
29373
29374 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29375     type : 'submit',
29376
29377     haveProgress : false,
29378     uploadComplete : false,
29379     
29380     // uploadProgress indicator.
29381     uploadProgress : function()
29382     {
29383         if (!this.form.progressUrl) {
29384             return;
29385         }
29386         
29387         if (!this.haveProgress) {
29388             Roo.MessageBox.progress("Uploading", "Uploading");
29389         }
29390         if (this.uploadComplete) {
29391            Roo.MessageBox.hide();
29392            return;
29393         }
29394         
29395         this.haveProgress = true;
29396    
29397         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29398         
29399         var c = new Roo.data.Connection();
29400         c.request({
29401             url : this.form.progressUrl,
29402             params: {
29403                 id : uid
29404             },
29405             method: 'GET',
29406             success : function(req){
29407                //console.log(data);
29408                 var rdata = false;
29409                 var edata;
29410                 try  {
29411                    rdata = Roo.decode(req.responseText)
29412                 } catch (e) {
29413                     Roo.log("Invalid data from server..");
29414                     Roo.log(edata);
29415                     return;
29416                 }
29417                 if (!rdata || !rdata.success) {
29418                     Roo.log(rdata);
29419                     Roo.MessageBox.alert(Roo.encode(rdata));
29420                     return;
29421                 }
29422                 var data = rdata.data;
29423                 
29424                 if (this.uploadComplete) {
29425                    Roo.MessageBox.hide();
29426                    return;
29427                 }
29428                    
29429                 if (data){
29430                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29431                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29432                     );
29433                 }
29434                 this.uploadProgress.defer(2000,this);
29435             },
29436        
29437             failure: function(data) {
29438                 Roo.log('progress url failed ');
29439                 Roo.log(data);
29440             },
29441             scope : this
29442         });
29443            
29444     },
29445     
29446     
29447     run : function()
29448     {
29449         // run get Values on the form, so it syncs any secondary forms.
29450         this.form.getValues();
29451         
29452         var o = this.options;
29453         var method = this.getMethod();
29454         var isPost = method == 'POST';
29455         if(o.clientValidation === false || this.form.isValid()){
29456             
29457             if (this.form.progressUrl) {
29458                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29459                     (new Date() * 1) + '' + Math.random());
29460                     
29461             } 
29462             
29463             
29464             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29465                 form:this.form.el.dom,
29466                 url:this.getUrl(!isPost),
29467                 method: method,
29468                 params:isPost ? this.getParams() : null,
29469                 isUpload: this.form.fileUpload
29470             }));
29471             
29472             this.uploadProgress();
29473
29474         }else if (o.clientValidation !== false){ // client validation failed
29475             this.failureType = Roo.form.Action.CLIENT_INVALID;
29476             this.form.afterAction(this, false);
29477         }
29478     },
29479
29480     success : function(response)
29481     {
29482         this.uploadComplete= true;
29483         if (this.haveProgress) {
29484             Roo.MessageBox.hide();
29485         }
29486         
29487         
29488         var result = this.processResponse(response);
29489         if(result === true || result.success){
29490             this.form.afterAction(this, true);
29491             return;
29492         }
29493         if(result.errors){
29494             this.form.markInvalid(result.errors);
29495             this.failureType = Roo.form.Action.SERVER_INVALID;
29496         }
29497         this.form.afterAction(this, false);
29498     },
29499     failure : function(response)
29500     {
29501         this.uploadComplete= true;
29502         if (this.haveProgress) {
29503             Roo.MessageBox.hide();
29504         }
29505         
29506         this.response = response;
29507         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29508         this.form.afterAction(this, false);
29509     },
29510     
29511     handleResponse : function(response){
29512         if(this.form.errorReader){
29513             var rs = this.form.errorReader.read(response);
29514             var errors = [];
29515             if(rs.records){
29516                 for(var i = 0, len = rs.records.length; i < len; i++) {
29517                     var r = rs.records[i];
29518                     errors[i] = r.data;
29519                 }
29520             }
29521             if(errors.length < 1){
29522                 errors = null;
29523             }
29524             return {
29525                 success : rs.success,
29526                 errors : errors
29527             };
29528         }
29529         var ret = false;
29530         try {
29531             ret = Roo.decode(response.responseText);
29532         } catch (e) {
29533             ret = {
29534                 success: false,
29535                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29536                 errors : []
29537             };
29538         }
29539         return ret;
29540         
29541     }
29542 });
29543
29544
29545 Roo.form.Action.Load = function(form, options){
29546     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29547     this.reader = this.form.reader;
29548 };
29549
29550 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29551     type : 'load',
29552
29553     run : function(){
29554         
29555         Roo.Ajax.request(Roo.apply(
29556                 this.createCallback(), {
29557                     method:this.getMethod(),
29558                     url:this.getUrl(false),
29559                     params:this.getParams()
29560         }));
29561     },
29562
29563     success : function(response){
29564         
29565         var result = this.processResponse(response);
29566         if(result === true || !result.success || !result.data){
29567             this.failureType = Roo.form.Action.LOAD_FAILURE;
29568             this.form.afterAction(this, false);
29569             return;
29570         }
29571         this.form.clearInvalid();
29572         this.form.setValues(result.data);
29573         this.form.afterAction(this, true);
29574     },
29575
29576     handleResponse : function(response){
29577         if(this.form.reader){
29578             var rs = this.form.reader.read(response);
29579             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29580             return {
29581                 success : rs.success,
29582                 data : data
29583             };
29584         }
29585         return Roo.decode(response.responseText);
29586     }
29587 });
29588
29589 Roo.form.Action.ACTION_TYPES = {
29590     'load' : Roo.form.Action.Load,
29591     'submit' : Roo.form.Action.Submit
29592 };/*
29593  * Based on:
29594  * Ext JS Library 1.1.1
29595  * Copyright(c) 2006-2007, Ext JS, LLC.
29596  *
29597  * Originally Released Under LGPL - original licence link has changed is not relivant.
29598  *
29599  * Fork - LGPL
29600  * <script type="text/javascript">
29601  */
29602  
29603 /**
29604  * @class Roo.form.Layout
29605  * @extends Roo.Component
29606  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29607  * @constructor
29608  * @param {Object} config Configuration options
29609  */
29610 Roo.form.Layout = function(config){
29611     var xitems = [];
29612     if (config.items) {
29613         xitems = config.items;
29614         delete config.items;
29615     }
29616     Roo.form.Layout.superclass.constructor.call(this, config);
29617     this.stack = [];
29618     Roo.each(xitems, this.addxtype, this);
29619      
29620 };
29621
29622 Roo.extend(Roo.form.Layout, Roo.Component, {
29623     /**
29624      * @cfg {String/Object} autoCreate
29625      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29626      */
29627     /**
29628      * @cfg {String/Object/Function} style
29629      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29630      * a function which returns such a specification.
29631      */
29632     /**
29633      * @cfg {String} labelAlign
29634      * Valid values are "left," "top" and "right" (defaults to "left")
29635      */
29636     /**
29637      * @cfg {Number} labelWidth
29638      * Fixed width in pixels of all field labels (defaults to undefined)
29639      */
29640     /**
29641      * @cfg {Boolean} clear
29642      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29643      */
29644     clear : true,
29645     /**
29646      * @cfg {String} labelSeparator
29647      * The separator to use after field labels (defaults to ':')
29648      */
29649     labelSeparator : ':',
29650     /**
29651      * @cfg {Boolean} hideLabels
29652      * True to suppress the display of field labels in this layout (defaults to false)
29653      */
29654     hideLabels : false,
29655
29656     // private
29657     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29658     
29659     isLayout : true,
29660     
29661     // private
29662     onRender : function(ct, position){
29663         if(this.el){ // from markup
29664             this.el = Roo.get(this.el);
29665         }else {  // generate
29666             var cfg = this.getAutoCreate();
29667             this.el = ct.createChild(cfg, position);
29668         }
29669         if(this.style){
29670             this.el.applyStyles(this.style);
29671         }
29672         if(this.labelAlign){
29673             this.el.addClass('x-form-label-'+this.labelAlign);
29674         }
29675         if(this.hideLabels){
29676             this.labelStyle = "display:none";
29677             this.elementStyle = "padding-left:0;";
29678         }else{
29679             if(typeof this.labelWidth == 'number'){
29680                 this.labelStyle = "width:"+this.labelWidth+"px;";
29681                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29682             }
29683             if(this.labelAlign == 'top'){
29684                 this.labelStyle = "width:auto;";
29685                 this.elementStyle = "padding-left:0;";
29686             }
29687         }
29688         var stack = this.stack;
29689         var slen = stack.length;
29690         if(slen > 0){
29691             if(!this.fieldTpl){
29692                 var t = new Roo.Template(
29693                     '<div class="x-form-item {5}">',
29694                         '<label for="{0}" style="{2}">{1}{4}</label>',
29695                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29696                         '</div>',
29697                     '</div><div class="x-form-clear-left"></div>'
29698                 );
29699                 t.disableFormats = true;
29700                 t.compile();
29701                 Roo.form.Layout.prototype.fieldTpl = t;
29702             }
29703             for(var i = 0; i < slen; i++) {
29704                 if(stack[i].isFormField){
29705                     this.renderField(stack[i]);
29706                 }else{
29707                     this.renderComponent(stack[i]);
29708                 }
29709             }
29710         }
29711         if(this.clear){
29712             this.el.createChild({cls:'x-form-clear'});
29713         }
29714     },
29715
29716     // private
29717     renderField : function(f){
29718         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29719                f.id, //0
29720                f.fieldLabel, //1
29721                f.labelStyle||this.labelStyle||'', //2
29722                this.elementStyle||'', //3
29723                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29724                f.itemCls||this.itemCls||''  //5
29725        ], true).getPrevSibling());
29726     },
29727
29728     // private
29729     renderComponent : function(c){
29730         c.render(c.isLayout ? this.el : this.el.createChild());    
29731     },
29732     /**
29733      * Adds a object form elements (using the xtype property as the factory method.)
29734      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29735      * @param {Object} config 
29736      */
29737     addxtype : function(o)
29738     {
29739         // create the lement.
29740         o.form = this.form;
29741         var fe = Roo.factory(o, Roo.form);
29742         this.form.allItems.push(fe);
29743         this.stack.push(fe);
29744         
29745         if (fe.isFormField) {
29746             this.form.items.add(fe);
29747         }
29748          
29749         return fe;
29750     }
29751 });
29752
29753 /**
29754  * @class Roo.form.Column
29755  * @extends Roo.form.Layout
29756  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29757  * @constructor
29758  * @param {Object} config Configuration options
29759  */
29760 Roo.form.Column = function(config){
29761     Roo.form.Column.superclass.constructor.call(this, config);
29762 };
29763
29764 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29765     /**
29766      * @cfg {Number/String} width
29767      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29768      */
29769     /**
29770      * @cfg {String/Object} autoCreate
29771      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29772      */
29773
29774     // private
29775     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29776
29777     // private
29778     onRender : function(ct, position){
29779         Roo.form.Column.superclass.onRender.call(this, ct, position);
29780         if(this.width){
29781             this.el.setWidth(this.width);
29782         }
29783     }
29784 });
29785
29786
29787 /**
29788  * @class Roo.form.Row
29789  * @extends Roo.form.Layout
29790  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29791  * @constructor
29792  * @param {Object} config Configuration options
29793  */
29794
29795  
29796 Roo.form.Row = function(config){
29797     Roo.form.Row.superclass.constructor.call(this, config);
29798 };
29799  
29800 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29801       /**
29802      * @cfg {Number/String} width
29803      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29804      */
29805     /**
29806      * @cfg {Number/String} height
29807      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29808      */
29809     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29810     
29811     padWidth : 20,
29812     // private
29813     onRender : function(ct, position){
29814         //console.log('row render');
29815         if(!this.rowTpl){
29816             var t = new Roo.Template(
29817                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29818                     '<label for="{0}" style="{2}">{1}{4}</label>',
29819                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29820                     '</div>',
29821                 '</div>'
29822             );
29823             t.disableFormats = true;
29824             t.compile();
29825             Roo.form.Layout.prototype.rowTpl = t;
29826         }
29827         this.fieldTpl = this.rowTpl;
29828         
29829         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29830         var labelWidth = 100;
29831         
29832         if ((this.labelAlign != 'top')) {
29833             if (typeof this.labelWidth == 'number') {
29834                 labelWidth = this.labelWidth
29835             }
29836             this.padWidth =  20 + labelWidth;
29837             
29838         }
29839         
29840         Roo.form.Column.superclass.onRender.call(this, ct, position);
29841         if(this.width){
29842             this.el.setWidth(this.width);
29843         }
29844         if(this.height){
29845             this.el.setHeight(this.height);
29846         }
29847     },
29848     
29849     // private
29850     renderField : function(f){
29851         f.fieldEl = this.fieldTpl.append(this.el, [
29852                f.id, f.fieldLabel,
29853                f.labelStyle||this.labelStyle||'',
29854                this.elementStyle||'',
29855                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29856                f.itemCls||this.itemCls||'',
29857                f.width ? f.width + this.padWidth : 160 + this.padWidth
29858        ],true);
29859     }
29860 });
29861  
29862
29863 /**
29864  * @class Roo.form.FieldSet
29865  * @extends Roo.form.Layout
29866  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29867  * @constructor
29868  * @param {Object} config Configuration options
29869  */
29870 Roo.form.FieldSet = function(config){
29871     Roo.form.FieldSet.superclass.constructor.call(this, config);
29872 };
29873
29874 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29875     /**
29876      * @cfg {String} legend
29877      * The text to display as the legend for the FieldSet (defaults to '')
29878      */
29879     /**
29880      * @cfg {String/Object} autoCreate
29881      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29882      */
29883
29884     // private
29885     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29886
29887     // private
29888     onRender : function(ct, position){
29889         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29890         if(this.legend){
29891             this.setLegend(this.legend);
29892         }
29893     },
29894
29895     // private
29896     setLegend : function(text){
29897         if(this.rendered){
29898             this.el.child('legend').update(text);
29899         }
29900     }
29901 });/*
29902  * Based on:
29903  * Ext JS Library 1.1.1
29904  * Copyright(c) 2006-2007, Ext JS, LLC.
29905  *
29906  * Originally Released Under LGPL - original licence link has changed is not relivant.
29907  *
29908  * Fork - LGPL
29909  * <script type="text/javascript">
29910  */
29911 /**
29912  * @class Roo.form.VTypes
29913  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29914  * @singleton
29915  */
29916 Roo.form.VTypes = function(){
29917     // closure these in so they are only created once.
29918     var alpha = /^[a-zA-Z_]+$/;
29919     var alphanum = /^[a-zA-Z0-9_]+$/;
29920     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29921     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29922
29923     // All these messages and functions are configurable
29924     return {
29925         /**
29926          * The function used to validate email addresses
29927          * @param {String} value The email address
29928          */
29929         'email' : function(v){
29930             return email.test(v);
29931         },
29932         /**
29933          * The error text to display when the email validation function returns false
29934          * @type String
29935          */
29936         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29937         /**
29938          * The keystroke filter mask to be applied on email input
29939          * @type RegExp
29940          */
29941         'emailMask' : /[a-z0-9_\.\-@]/i,
29942
29943         /**
29944          * The function used to validate URLs
29945          * @param {String} value The URL
29946          */
29947         'url' : function(v){
29948             return url.test(v);
29949         },
29950         /**
29951          * The error text to display when the url validation function returns false
29952          * @type String
29953          */
29954         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29955         
29956         /**
29957          * The function used to validate alpha values
29958          * @param {String} value The value
29959          */
29960         'alpha' : function(v){
29961             return alpha.test(v);
29962         },
29963         /**
29964          * The error text to display when the alpha validation function returns false
29965          * @type String
29966          */
29967         'alphaText' : 'This field should only contain letters and _',
29968         /**
29969          * The keystroke filter mask to be applied on alpha input
29970          * @type RegExp
29971          */
29972         'alphaMask' : /[a-z_]/i,
29973
29974         /**
29975          * The function used to validate alphanumeric values
29976          * @param {String} value The value
29977          */
29978         'alphanum' : function(v){
29979             return alphanum.test(v);
29980         },
29981         /**
29982          * The error text to display when the alphanumeric validation function returns false
29983          * @type String
29984          */
29985         'alphanumText' : 'This field should only contain letters, numbers and _',
29986         /**
29987          * The keystroke filter mask to be applied on alphanumeric input
29988          * @type RegExp
29989          */
29990         'alphanumMask' : /[a-z0-9_]/i
29991     };
29992 }();//<script type="text/javascript">
29993
29994 /**
29995  * @class Roo.form.FCKeditor
29996  * @extends Roo.form.TextArea
29997  * Wrapper around the FCKEditor http://www.fckeditor.net
29998  * @constructor
29999  * Creates a new FCKeditor
30000  * @param {Object} config Configuration options
30001  */
30002 Roo.form.FCKeditor = function(config){
30003     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30004     this.addEvents({
30005          /**
30006          * @event editorinit
30007          * Fired when the editor is initialized - you can add extra handlers here..
30008          * @param {FCKeditor} this
30009          * @param {Object} the FCK object.
30010          */
30011         editorinit : true
30012     });
30013     
30014     
30015 };
30016 Roo.form.FCKeditor.editors = { };
30017 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30018 {
30019     //defaultAutoCreate : {
30020     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30021     //},
30022     // private
30023     /**
30024      * @cfg {Object} fck options - see fck manual for details.
30025      */
30026     fckconfig : false,
30027     
30028     /**
30029      * @cfg {Object} fck toolbar set (Basic or Default)
30030      */
30031     toolbarSet : 'Basic',
30032     /**
30033      * @cfg {Object} fck BasePath
30034      */ 
30035     basePath : '/fckeditor/',
30036     
30037     
30038     frame : false,
30039     
30040     value : '',
30041     
30042    
30043     onRender : function(ct, position)
30044     {
30045         if(!this.el){
30046             this.defaultAutoCreate = {
30047                 tag: "textarea",
30048                 style:"width:300px;height:60px;",
30049                 autocomplete: "off"
30050             };
30051         }
30052         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30053         /*
30054         if(this.grow){
30055             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30056             if(this.preventScrollbars){
30057                 this.el.setStyle("overflow", "hidden");
30058             }
30059             this.el.setHeight(this.growMin);
30060         }
30061         */
30062         //console.log('onrender' + this.getId() );
30063         Roo.form.FCKeditor.editors[this.getId()] = this;
30064          
30065
30066         this.replaceTextarea() ;
30067         
30068     },
30069     
30070     getEditor : function() {
30071         return this.fckEditor;
30072     },
30073     /**
30074      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30075      * @param {Mixed} value The value to set
30076      */
30077     
30078     
30079     setValue : function(value)
30080     {
30081         //console.log('setValue: ' + value);
30082         
30083         if(typeof(value) == 'undefined') { // not sure why this is happending...
30084             return;
30085         }
30086         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30087         
30088         //if(!this.el || !this.getEditor()) {
30089         //    this.value = value;
30090             //this.setValue.defer(100,this,[value]);    
30091         //    return;
30092         //} 
30093         
30094         if(!this.getEditor()) {
30095             return;
30096         }
30097         
30098         this.getEditor().SetData(value);
30099         
30100         //
30101
30102     },
30103
30104     /**
30105      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30106      * @return {Mixed} value The field value
30107      */
30108     getValue : function()
30109     {
30110         
30111         if (this.frame && this.frame.dom.style.display == 'none') {
30112             return Roo.form.FCKeditor.superclass.getValue.call(this);
30113         }
30114         
30115         if(!this.el || !this.getEditor()) {
30116            
30117            // this.getValue.defer(100,this); 
30118             return this.value;
30119         }
30120        
30121         
30122         var value=this.getEditor().GetData();
30123         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30124         return Roo.form.FCKeditor.superclass.getValue.call(this);
30125         
30126
30127     },
30128
30129     /**
30130      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30131      * @return {Mixed} value The field value
30132      */
30133     getRawValue : function()
30134     {
30135         if (this.frame && this.frame.dom.style.display == 'none') {
30136             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30137         }
30138         
30139         if(!this.el || !this.getEditor()) {
30140             //this.getRawValue.defer(100,this); 
30141             return this.value;
30142             return;
30143         }
30144         
30145         
30146         
30147         var value=this.getEditor().GetData();
30148         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30149         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30150          
30151     },
30152     
30153     setSize : function(w,h) {
30154         
30155         
30156         
30157         //if (this.frame && this.frame.dom.style.display == 'none') {
30158         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30159         //    return;
30160         //}
30161         //if(!this.el || !this.getEditor()) {
30162         //    this.setSize.defer(100,this, [w,h]); 
30163         //    return;
30164         //}
30165         
30166         
30167         
30168         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30169         
30170         this.frame.dom.setAttribute('width', w);
30171         this.frame.dom.setAttribute('height', h);
30172         this.frame.setSize(w,h);
30173         
30174     },
30175     
30176     toggleSourceEdit : function(value) {
30177         
30178       
30179          
30180         this.el.dom.style.display = value ? '' : 'none';
30181         this.frame.dom.style.display = value ?  'none' : '';
30182         
30183     },
30184     
30185     
30186     focus: function(tag)
30187     {
30188         if (this.frame.dom.style.display == 'none') {
30189             return Roo.form.FCKeditor.superclass.focus.call(this);
30190         }
30191         if(!this.el || !this.getEditor()) {
30192             this.focus.defer(100,this, [tag]); 
30193             return;
30194         }
30195         
30196         
30197         
30198         
30199         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30200         this.getEditor().Focus();
30201         if (tgs.length) {
30202             if (!this.getEditor().Selection.GetSelection()) {
30203                 this.focus.defer(100,this, [tag]); 
30204                 return;
30205             }
30206             
30207             
30208             var r = this.getEditor().EditorDocument.createRange();
30209             r.setStart(tgs[0],0);
30210             r.setEnd(tgs[0],0);
30211             this.getEditor().Selection.GetSelection().removeAllRanges();
30212             this.getEditor().Selection.GetSelection().addRange(r);
30213             this.getEditor().Focus();
30214         }
30215         
30216     },
30217     
30218     
30219     
30220     replaceTextarea : function()
30221     {
30222         if ( document.getElementById( this.getId() + '___Frame' ) )
30223             return ;
30224         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30225         //{
30226             // We must check the elements firstly using the Id and then the name.
30227         var oTextarea = document.getElementById( this.getId() );
30228         
30229         var colElementsByName = document.getElementsByName( this.getId() ) ;
30230          
30231         oTextarea.style.display = 'none' ;
30232
30233         if ( oTextarea.tabIndex ) {            
30234             this.TabIndex = oTextarea.tabIndex ;
30235         }
30236         
30237         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30238         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30239         this.frame = Roo.get(this.getId() + '___Frame')
30240     },
30241     
30242     _getConfigHtml : function()
30243     {
30244         var sConfig = '' ;
30245
30246         for ( var o in this.fckconfig ) {
30247             sConfig += sConfig.length > 0  ? '&amp;' : '';
30248             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30249         }
30250
30251         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30252     },
30253     
30254     
30255     _getIFrameHtml : function()
30256     {
30257         var sFile = 'fckeditor.html' ;
30258         /* no idea what this is about..
30259         try
30260         {
30261             if ( (/fcksource=true/i).test( window.top.location.search ) )
30262                 sFile = 'fckeditor.original.html' ;
30263         }
30264         catch (e) { 
30265         */
30266
30267         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30268         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30269         
30270         
30271         var html = '<iframe id="' + this.getId() +
30272             '___Frame" src="' + sLink +
30273             '" width="' + this.width +
30274             '" height="' + this.height + '"' +
30275             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30276             ' frameborder="0" scrolling="no"></iframe>' ;
30277
30278         return html ;
30279     },
30280     
30281     _insertHtmlBefore : function( html, element )
30282     {
30283         if ( element.insertAdjacentHTML )       {
30284             // IE
30285             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30286         } else { // Gecko
30287             var oRange = document.createRange() ;
30288             oRange.setStartBefore( element ) ;
30289             var oFragment = oRange.createContextualFragment( html );
30290             element.parentNode.insertBefore( oFragment, element ) ;
30291         }
30292     }
30293     
30294     
30295   
30296     
30297     
30298     
30299     
30300
30301 });
30302
30303 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30304
30305 function FCKeditor_OnComplete(editorInstance){
30306     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30307     f.fckEditor = editorInstance;
30308     //console.log("loaded");
30309     f.fireEvent('editorinit', f, editorInstance);
30310
30311   
30312
30313  
30314
30315
30316
30317
30318
30319
30320
30321
30322
30323
30324
30325
30326
30327
30328
30329 //<script type="text/javascript">
30330 /**
30331  * @class Roo.form.GridField
30332  * @extends Roo.form.Field
30333  * Embed a grid (or editable grid into a form)
30334  * STATUS ALPHA
30335  * 
30336  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30337  * it needs 
30338  * xgrid.store = Roo.data.Store
30339  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30340  * xgrid.store.reader = Roo.data.JsonReader 
30341  * 
30342  * 
30343  * @constructor
30344  * Creates a new GridField
30345  * @param {Object} config Configuration options
30346  */
30347 Roo.form.GridField = function(config){
30348     Roo.form.GridField.superclass.constructor.call(this, config);
30349      
30350 };
30351
30352 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30353     /**
30354      * @cfg {Number} width  - used to restrict width of grid..
30355      */
30356     width : 100,
30357     /**
30358      * @cfg {Number} height - used to restrict height of grid..
30359      */
30360     height : 50,
30361      /**
30362      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30363          * 
30364          *}
30365      */
30366     xgrid : false, 
30367     /**
30368      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30369      * {tag: "input", type: "checkbox", autocomplete: "off"})
30370      */
30371    // defaultAutoCreate : { tag: 'div' },
30372     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30373     /**
30374      * @cfg {String} addTitle Text to include for adding a title.
30375      */
30376     addTitle : false,
30377     //
30378     onResize : function(){
30379         Roo.form.Field.superclass.onResize.apply(this, arguments);
30380     },
30381
30382     initEvents : function(){
30383         // Roo.form.Checkbox.superclass.initEvents.call(this);
30384         // has no events...
30385        
30386     },
30387
30388
30389     getResizeEl : function(){
30390         return this.wrap;
30391     },
30392
30393     getPositionEl : function(){
30394         return this.wrap;
30395     },
30396
30397     // private
30398     onRender : function(ct, position){
30399         
30400         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30401         var style = this.style;
30402         delete this.style;
30403         
30404         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30405         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30406         this.viewEl = this.wrap.createChild({ tag: 'div' });
30407         if (style) {
30408             this.viewEl.applyStyles(style);
30409         }
30410         if (this.width) {
30411             this.viewEl.setWidth(this.width);
30412         }
30413         if (this.height) {
30414             this.viewEl.setHeight(this.height);
30415         }
30416         //if(this.inputValue !== undefined){
30417         //this.setValue(this.value);
30418         
30419         
30420         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30421         
30422         
30423         this.grid.render();
30424         this.grid.getDataSource().on('remove', this.refreshValue, this);
30425         this.grid.getDataSource().on('update', this.refreshValue, this);
30426         this.grid.on('afteredit', this.refreshValue, this);
30427  
30428     },
30429      
30430     
30431     /**
30432      * Sets the value of the item. 
30433      * @param {String} either an object  or a string..
30434      */
30435     setValue : function(v){
30436         //this.value = v;
30437         v = v || []; // empty set..
30438         // this does not seem smart - it really only affects memoryproxy grids..
30439         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30440             var ds = this.grid.getDataSource();
30441             // assumes a json reader..
30442             var data = {}
30443             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30444             ds.loadData( data);
30445         }
30446         // clear selection so it does not get stale.
30447         if (this.grid.sm) { 
30448             this.grid.sm.clearSelections();
30449         }
30450         
30451         Roo.form.GridField.superclass.setValue.call(this, v);
30452         this.refreshValue();
30453         // should load data in the grid really....
30454     },
30455     
30456     // private
30457     refreshValue: function() {
30458          var val = [];
30459         this.grid.getDataSource().each(function(r) {
30460             val.push(r.data);
30461         });
30462         this.el.dom.value = Roo.encode(val);
30463     }
30464     
30465      
30466     
30467     
30468 });/*
30469  * Based on:
30470  * Ext JS Library 1.1.1
30471  * Copyright(c) 2006-2007, Ext JS, LLC.
30472  *
30473  * Originally Released Under LGPL - original licence link has changed is not relivant.
30474  *
30475  * Fork - LGPL
30476  * <script type="text/javascript">
30477  */
30478 /**
30479  * @class Roo.form.DisplayField
30480  * @extends Roo.form.Field
30481  * A generic Field to display non-editable data.
30482  * @constructor
30483  * Creates a new Display Field item.
30484  * @param {Object} config Configuration options
30485  */
30486 Roo.form.DisplayField = function(config){
30487     Roo.form.DisplayField.superclass.constructor.call(this, config);
30488     
30489 };
30490
30491 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30492     inputType:      'hidden',
30493     allowBlank:     true,
30494     readOnly:         true,
30495     
30496  
30497     /**
30498      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30499      */
30500     focusClass : undefined,
30501     /**
30502      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30503      */
30504     fieldClass: 'x-form-field',
30505     
30506      /**
30507      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30508      */
30509     valueRenderer: undefined,
30510     
30511     width: 100,
30512     /**
30513      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30514      * {tag: "input", type: "checkbox", autocomplete: "off"})
30515      */
30516      
30517  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30518
30519     onResize : function(){
30520         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30521         
30522     },
30523
30524     initEvents : function(){
30525         // Roo.form.Checkbox.superclass.initEvents.call(this);
30526         // has no events...
30527        
30528     },
30529
30530
30531     getResizeEl : function(){
30532         return this.wrap;
30533     },
30534
30535     getPositionEl : function(){
30536         return this.wrap;
30537     },
30538
30539     // private
30540     onRender : function(ct, position){
30541         
30542         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30543         //if(this.inputValue !== undefined){
30544         this.wrap = this.el.wrap();
30545         
30546         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30547         
30548         if (this.bodyStyle) {
30549             this.viewEl.applyStyles(this.bodyStyle);
30550         }
30551         //this.viewEl.setStyle('padding', '2px');
30552         
30553         this.setValue(this.value);
30554         
30555     },
30556 /*
30557     // private
30558     initValue : Roo.emptyFn,
30559
30560   */
30561
30562         // private
30563     onClick : function(){
30564         
30565     },
30566
30567     /**
30568      * Sets the checked state of the checkbox.
30569      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30570      */
30571     setValue : function(v){
30572         this.value = v;
30573         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30574         // this might be called before we have a dom element..
30575         if (!this.viewEl) {
30576             return;
30577         }
30578         this.viewEl.dom.innerHTML = html;
30579         Roo.form.DisplayField.superclass.setValue.call(this, v);
30580
30581     }
30582 });/*
30583  * 
30584  * Licence- LGPL
30585  * 
30586  */
30587
30588 /**
30589  * @class Roo.form.DayPicker
30590  * @extends Roo.form.Field
30591  * A Day picker show [M] [T] [W] ....
30592  * @constructor
30593  * Creates a new Day Picker
30594  * @param {Object} config Configuration options
30595  */
30596 Roo.form.DayPicker= function(config){
30597     Roo.form.DayPicker.superclass.constructor.call(this, config);
30598      
30599 };
30600
30601 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30602     /**
30603      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30604      */
30605     focusClass : undefined,
30606     /**
30607      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30608      */
30609     fieldClass: "x-form-field",
30610    
30611     /**
30612      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30613      * {tag: "input", type: "checkbox", autocomplete: "off"})
30614      */
30615     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30616     
30617    
30618     actionMode : 'viewEl', 
30619     //
30620     // private
30621  
30622     inputType : 'hidden',
30623     
30624      
30625     inputElement: false, // real input element?
30626     basedOn: false, // ????
30627     
30628     isFormField: true, // not sure where this is needed!!!!
30629
30630     onResize : function(){
30631         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30632         if(!this.boxLabel){
30633             this.el.alignTo(this.wrap, 'c-c');
30634         }
30635     },
30636
30637     initEvents : function(){
30638         Roo.form.Checkbox.superclass.initEvents.call(this);
30639         this.el.on("click", this.onClick,  this);
30640         this.el.on("change", this.onClick,  this);
30641     },
30642
30643
30644     getResizeEl : function(){
30645         return this.wrap;
30646     },
30647
30648     getPositionEl : function(){
30649         return this.wrap;
30650     },
30651
30652     
30653     // private
30654     onRender : function(ct, position){
30655         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30656        
30657         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30658         
30659         var r1 = '<table><tr>';
30660         var r2 = '<tr class="x-form-daypick-icons">';
30661         for (var i=0; i < 7; i++) {
30662             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30663             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30664         }
30665         
30666         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30667         viewEl.select('img').on('click', this.onClick, this);
30668         this.viewEl = viewEl;   
30669         
30670         
30671         // this will not work on Chrome!!!
30672         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30673         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30674         
30675         
30676           
30677
30678     },
30679
30680     // private
30681     initValue : Roo.emptyFn,
30682
30683     /**
30684      * Returns the checked state of the checkbox.
30685      * @return {Boolean} True if checked, else false
30686      */
30687     getValue : function(){
30688         return this.el.dom.value;
30689         
30690     },
30691
30692         // private
30693     onClick : function(e){ 
30694         //this.setChecked(!this.checked);
30695         Roo.get(e.target).toggleClass('x-menu-item-checked');
30696         this.refreshValue();
30697         //if(this.el.dom.checked != this.checked){
30698         //    this.setValue(this.el.dom.checked);
30699        // }
30700     },
30701     
30702     // private
30703     refreshValue : function()
30704     {
30705         var val = '';
30706         this.viewEl.select('img',true).each(function(e,i,n)  {
30707             val += e.is(".x-menu-item-checked") ? String(n) : '';
30708         });
30709         this.setValue(val, true);
30710     },
30711
30712     /**
30713      * Sets the checked state of the checkbox.
30714      * On is always based on a string comparison between inputValue and the param.
30715      * @param {Boolean/String} value - the value to set 
30716      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30717      */
30718     setValue : function(v,suppressEvent){
30719         if (!this.el.dom) {
30720             return;
30721         }
30722         var old = this.el.dom.value ;
30723         this.el.dom.value = v;
30724         if (suppressEvent) {
30725             return ;
30726         }
30727          
30728         // update display..
30729         this.viewEl.select('img',true).each(function(e,i,n)  {
30730             
30731             var on = e.is(".x-menu-item-checked");
30732             var newv = v.indexOf(String(n)) > -1;
30733             if (on != newv) {
30734                 e.toggleClass('x-menu-item-checked');
30735             }
30736             
30737         });
30738         
30739         
30740         this.fireEvent('change', this, v, old);
30741         
30742         
30743     },
30744    
30745     // handle setting of hidden value by some other method!!?!?
30746     setFromHidden: function()
30747     {
30748         if(!this.el){
30749             return;
30750         }
30751         //console.log("SET FROM HIDDEN");
30752         //alert('setFrom hidden');
30753         this.setValue(this.el.dom.value);
30754     },
30755     
30756     onDestroy : function()
30757     {
30758         if(this.viewEl){
30759             Roo.get(this.viewEl).remove();
30760         }
30761          
30762         Roo.form.DayPicker.superclass.onDestroy.call(this);
30763     }
30764
30765 });/*
30766  * RooJS Library 1.1.1
30767  * Copyright(c) 2008-2011  Alan Knowles
30768  *
30769  * License - LGPL
30770  */
30771  
30772
30773 /**
30774  * @class Roo.form.ComboCheck
30775  * @extends Roo.form.ComboBox
30776  * A combobox for multiple select items.
30777  *
30778  * FIXME - could do with a reset button..
30779  * 
30780  * @constructor
30781  * Create a new ComboCheck
30782  * @param {Object} config Configuration options
30783  */
30784 Roo.form.ComboCheck = function(config){
30785     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30786     // should verify some data...
30787     // like
30788     // hiddenName = required..
30789     // displayField = required
30790     // valudField == required
30791     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30792     var _t = this;
30793     Roo.each(req, function(e) {
30794         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30795             throw "Roo.form.ComboCheck : missing value for: " + e;
30796         }
30797     });
30798     
30799     
30800 };
30801
30802 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30803      
30804      
30805     editable : false,
30806      
30807     selectedClass: 'x-menu-item-checked', 
30808     
30809     // private
30810     onRender : function(ct, position){
30811         var _t = this;
30812         
30813         
30814         
30815         if(!this.tpl){
30816             var cls = 'x-combo-list';
30817
30818             
30819             this.tpl =  new Roo.Template({
30820                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30821                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30822                    '<span>{' + this.displayField + '}</span>' +
30823                     '</div>' 
30824                 
30825             });
30826         }
30827  
30828         
30829         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30830         this.view.singleSelect = false;
30831         this.view.multiSelect = true;
30832         this.view.toggleSelect = true;
30833         this.pageTb.add(new Roo.Toolbar.Fill(), {
30834             
30835             text: 'Done',
30836             handler: function()
30837             {
30838                 _t.collapse();
30839             }
30840         });
30841     },
30842     
30843     onViewOver : function(e, t){
30844         // do nothing...
30845         return;
30846         
30847     },
30848     
30849     onViewClick : function(doFocus,index){
30850         return;
30851         
30852     },
30853     select: function () {
30854         //Roo.log("SELECT CALLED");
30855     },
30856      
30857     selectByValue : function(xv, scrollIntoView){
30858         var ar = this.getValueArray();
30859         var sels = [];
30860         
30861         Roo.each(ar, function(v) {
30862             if(v === undefined || v === null){
30863                 return;
30864             }
30865             var r = this.findRecord(this.valueField, v);
30866             if(r){
30867                 sels.push(this.store.indexOf(r))
30868                 
30869             }
30870         },this);
30871         this.view.select(sels);
30872         return false;
30873     },
30874     
30875     
30876     
30877     onSelect : function(record, index){
30878        // Roo.log("onselect Called");
30879        // this is only called by the clear button now..
30880         this.view.clearSelections();
30881         this.setValue('[]');
30882         if (this.value != this.valueBefore) {
30883             this.fireEvent('change', this, this.value, this.valueBefore);
30884             this.valueBefore = this.value;
30885         }
30886     },
30887     getValueArray : function()
30888     {
30889         var ar = [] ;
30890         
30891         try {
30892             //Roo.log(this.value);
30893             if (typeof(this.value) == 'undefined') {
30894                 return [];
30895             }
30896             var ar = Roo.decode(this.value);
30897             return  ar instanceof Array ? ar : []; //?? valid?
30898             
30899         } catch(e) {
30900             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30901             return [];
30902         }
30903          
30904     },
30905     expand : function ()
30906     {
30907         
30908         Roo.form.ComboCheck.superclass.expand.call(this);
30909         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30910         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30911         
30912
30913     },
30914     
30915     collapse : function(){
30916         Roo.form.ComboCheck.superclass.collapse.call(this);
30917         var sl = this.view.getSelectedIndexes();
30918         var st = this.store;
30919         var nv = [];
30920         var tv = [];
30921         var r;
30922         Roo.each(sl, function(i) {
30923             r = st.getAt(i);
30924             nv.push(r.get(this.valueField));
30925         },this);
30926         this.setValue(Roo.encode(nv));
30927         if (this.value != this.valueBefore) {
30928
30929             this.fireEvent('change', this, this.value, this.valueBefore);
30930             this.valueBefore = this.value;
30931         }
30932         
30933     },
30934     
30935     setValue : function(v){
30936         // Roo.log(v);
30937         this.value = v;
30938         
30939         var vals = this.getValueArray();
30940         var tv = [];
30941         Roo.each(vals, function(k) {
30942             var r = this.findRecord(this.valueField, k);
30943             if(r){
30944                 tv.push(r.data[this.displayField]);
30945             }else if(this.valueNotFoundText !== undefined){
30946                 tv.push( this.valueNotFoundText );
30947             }
30948         },this);
30949        // Roo.log(tv);
30950         
30951         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30952         this.hiddenField.value = v;
30953         this.value = v;
30954     }
30955     
30956 });/*
30957  * Based on:
30958  * Ext JS Library 1.1.1
30959  * Copyright(c) 2006-2007, Ext JS, LLC.
30960  *
30961  * Originally Released Under LGPL - original licence link has changed is not relivant.
30962  *
30963  * Fork - LGPL
30964  * <script type="text/javascript">
30965  */
30966  
30967 /**
30968  * @class Roo.form.Signature
30969  * @extends Roo.form.Field
30970  * Signature field.  
30971  * @constructor
30972  * 
30973  * @param {Object} config Configuration options
30974  */
30975
30976 Roo.form.Signature = function(config){
30977     Roo.form.Signature.superclass.constructor.call(this, config);
30978     
30979     this.addEvents({// not in used??
30980          /**
30981          * @event confirm
30982          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30983              * @param {Roo.form.Signature} combo This combo box
30984              */
30985         'confirm' : true,
30986         /**
30987          * @event reset
30988          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30989              * @param {Roo.form.ComboBox} combo This combo box
30990              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30991              */
30992         'reset' : true
30993     });
30994 };
30995
30996 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30997     /**
30998      * @cfg {Object} labels Label to use when rendering a form.
30999      * defaults to 
31000      * labels : { 
31001      *      clear : "Clear",
31002      *      confirm : "Confirm"
31003      *  }
31004      */
31005     labels : { 
31006         clear : "Clear",
31007         confirm : "Confirm"
31008     },
31009     /**
31010      * @cfg {Number} width The signature panel width (defaults to 300)
31011      */
31012     width: 300,
31013     /**
31014      * @cfg {Number} height The signature panel height (defaults to 100)
31015      */
31016     height : 100,
31017     /**
31018      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31019      */
31020     allowBlank : false,
31021     
31022     //private
31023     // {Object} signPanel The signature SVG panel element (defaults to {})
31024     signPanel : {},
31025     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31026     isMouseDown : false,
31027     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31028     isConfirmed : false,
31029     // {String} signatureTmp SVG mapping string (defaults to empty string)
31030     signatureTmp : '',
31031     
31032     
31033     defaultAutoCreate : { // modified by initCompnoent..
31034         tag: "input",
31035         type:"hidden"
31036     },
31037
31038     // private
31039     onRender : function(ct, position){
31040         
31041         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31042         
31043         this.wrap = this.el.wrap({
31044             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31045         });
31046         
31047         this.createToolbar(this);
31048         this.signPanel = this.wrap.createChild({
31049                 tag: 'div',
31050                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31051             }, this.el
31052         );
31053             
31054         this.svgID = Roo.id();
31055         this.svgEl = this.signPanel.createChild({
31056               xmlns : 'http://www.w3.org/2000/svg',
31057               tag : 'svg',
31058               id : this.svgID + "-svg",
31059               width: this.width,
31060               height: this.height,
31061               viewBox: '0 0 '+this.width+' '+this.height,
31062               cn : [
31063                 {
31064                     tag: "rect",
31065                     id: this.svgID + "-svg-r",
31066                     width: this.width,
31067                     height: this.height,
31068                     fill: "#ffa"
31069                 },
31070                 {
31071                     tag: "line",
31072                     id: this.svgID + "-svg-l",
31073                     x1: "0", // start
31074                     y1: (this.height*0.8), // start set the line in 80% of height
31075                     x2: this.width, // end
31076                     y2: (this.height*0.8), // end set the line in 80% of height
31077                     'stroke': "#666",
31078                     'stroke-width': "1",
31079                     'stroke-dasharray': "3",
31080                     'shape-rendering': "crispEdges",
31081                     'pointer-events': "none"
31082                 },
31083                 {
31084                     tag: "path",
31085                     id: this.svgID + "-svg-p",
31086                     'stroke': "navy",
31087                     'stroke-width': "3",
31088                     'fill': "none",
31089                     'pointer-events': 'none'
31090                 }
31091               ]
31092         });
31093         this.createSVG();
31094         this.svgBox = this.svgEl.dom.getScreenCTM();
31095     },
31096     createSVG : function(){ 
31097         var svg = this.signPanel;
31098         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31099         var t = this;
31100
31101         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31102         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31103         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31104         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31105         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31106         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31107         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31108         
31109     },
31110     isTouchEvent : function(e){
31111         return e.type.match(/^touch/);
31112     },
31113     getCoords : function (e) {
31114         var pt    = this.svgEl.dom.createSVGPoint();
31115         pt.x = e.clientX; 
31116         pt.y = e.clientY;
31117         if (this.isTouchEvent(e)) {
31118             pt.x =  e.targetTouches[0].clientX 
31119             pt.y = e.targetTouches[0].clientY;
31120         }
31121         var a = this.svgEl.dom.getScreenCTM();
31122         var b = a.inverse();
31123         var mx = pt.matrixTransform(b);
31124         return mx.x + ',' + mx.y;
31125     },
31126     //mouse event headler 
31127     down : function (e) {
31128         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31129         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31130         
31131         this.isMouseDown = true;
31132         
31133         e.preventDefault();
31134     },
31135     move : function (e) {
31136         if (this.isMouseDown) {
31137             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31138             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31139         }
31140         
31141         e.preventDefault();
31142     },
31143     up : function (e) {
31144         this.isMouseDown = false;
31145         var sp = this.signatureTmp.split(' ');
31146         
31147         if(sp.length > 1){
31148             if(!sp[sp.length-2].match(/^L/)){
31149                 sp.pop();
31150                 sp.pop();
31151                 sp.push("");
31152                 this.signatureTmp = sp.join(" ");
31153             }
31154         }
31155         if(this.getValue() != this.signatureTmp){
31156             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31157             this.isConfirmed = false;
31158         }
31159         e.preventDefault();
31160     },
31161     
31162     /**
31163      * Protected method that will not generally be called directly. It
31164      * is called when the editor creates its toolbar. Override this method if you need to
31165      * add custom toolbar buttons.
31166      * @param {HtmlEditor} editor
31167      */
31168     createToolbar : function(editor){
31169          function btn(id, toggle, handler){
31170             var xid = fid + '-'+ id ;
31171             return {
31172                 id : xid,
31173                 cmd : id,
31174                 cls : 'x-btn-icon x-edit-'+id,
31175                 enableToggle:toggle !== false,
31176                 scope: editor, // was editor...
31177                 handler:handler||editor.relayBtnCmd,
31178                 clickEvent:'mousedown',
31179                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31180                 tabIndex:-1
31181             };
31182         }
31183         
31184         
31185         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31186         this.tb = tb;
31187         this.tb.add(
31188            {
31189                 cls : ' x-signature-btn x-signature-'+id,
31190                 scope: editor, // was editor...
31191                 handler: this.reset,
31192                 clickEvent:'mousedown',
31193                 text: this.labels.clear
31194             },
31195             {
31196                  xtype : 'Fill',
31197                  xns: Roo.Toolbar
31198             }, 
31199             {
31200                 cls : '  x-signature-btn x-signature-'+id,
31201                 scope: editor, // was editor...
31202                 handler: this.confirmHandler,
31203                 clickEvent:'mousedown',
31204                 text: this.labels.confirm
31205             }
31206         );
31207     
31208     },
31209     //public
31210     /**
31211      * when user is clicked confirm then show this image.....
31212      * 
31213      * @return {String} Image Data URI
31214      */
31215     getImageDataURI : function(){
31216         var svg = this.svgEl.dom.parentNode.innerHTML;
31217         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31218         return src; 
31219     },
31220     /**
31221      * 
31222      * @return {Boolean} this.isConfirmed
31223      */
31224     getConfirmed : function(){
31225         return this.isConfirmed;
31226     },
31227     /**
31228      * 
31229      * @return {Number} this.width
31230      */
31231     getWidth : function(){
31232         return this.width;
31233     },
31234     /**
31235      * 
31236      * @return {Number} this.height
31237      */
31238     getHeight : function(){
31239         return this.height;
31240     },
31241     // private
31242     getSignature : function(){
31243         return this.signatureTmp;
31244     },
31245     // private
31246     reset : function(){
31247         this.signatureTmp = '';
31248         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31249         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31250         this.isConfirmed = false;
31251         Roo.form.Signature.superclass.reset.call(this);
31252     },
31253     setSignature : function(s){
31254         this.signatureTmp = s;
31255         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31256         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31257         this.setValue(s);
31258         this.isConfirmed = false;
31259         Roo.form.Signature.superclass.reset.call(this);
31260     }, 
31261     test : function(){
31262 //        Roo.log(this.signPanel.dom.contentWindow.up())
31263     },
31264     //private
31265     setConfirmed : function(){
31266         
31267         
31268         
31269 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31270     },
31271     // private
31272     confirmHandler : function(){
31273         if(!this.getSignature()){
31274             return;
31275         }
31276         
31277         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31278         this.setValue(this.getSignature());
31279         this.isConfirmed = true;
31280         
31281         this.fireEvent('confirm', this);
31282     },
31283     // private
31284     // Subclasses should provide the validation implementation by overriding this
31285     validateValue : function(value){
31286         if(this.allowBlank){
31287             return true;
31288         }
31289         
31290         if(this.isConfirmed){
31291             return true;
31292         }
31293         return false;
31294     }
31295 });/*
31296  * Based on:
31297  * Ext JS Library 1.1.1
31298  * Copyright(c) 2006-2007, Ext JS, LLC.
31299  *
31300  * Originally Released Under LGPL - original licence link has changed is not relivant.
31301  *
31302  * Fork - LGPL
31303  * <script type="text/javascript">
31304  */
31305  
31306
31307 /**
31308  * @class Roo.form.ComboBox
31309  * @extends Roo.form.TriggerField
31310  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31311  * @constructor
31312  * Create a new ComboBox.
31313  * @param {Object} config Configuration options
31314  */
31315 Roo.form.Select = function(config){
31316     Roo.form.Select.superclass.constructor.call(this, config);
31317      
31318 };
31319
31320 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31321     /**
31322      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31323      */
31324     /**
31325      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31326      * rendering into an Roo.Editor, defaults to false)
31327      */
31328     /**
31329      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31330      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31331      */
31332     /**
31333      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31334      */
31335     /**
31336      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31337      * the dropdown list (defaults to undefined, with no header element)
31338      */
31339
31340      /**
31341      * @cfg {String/Roo.Template} tpl The template to use to render the output
31342      */
31343      
31344     // private
31345     defaultAutoCreate : {tag: "select"  },
31346     /**
31347      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31348      */
31349     listWidth: undefined,
31350     /**
31351      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31352      * mode = 'remote' or 'text' if mode = 'local')
31353      */
31354     displayField: undefined,
31355     /**
31356      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31357      * mode = 'remote' or 'value' if mode = 'local'). 
31358      * Note: use of a valueField requires the user make a selection
31359      * in order for a value to be mapped.
31360      */
31361     valueField: undefined,
31362     
31363     
31364     /**
31365      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31366      * field's data value (defaults to the underlying DOM element's name)
31367      */
31368     hiddenName: undefined,
31369     /**
31370      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31371      */
31372     listClass: '',
31373     /**
31374      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31375      */
31376     selectedClass: 'x-combo-selected',
31377     /**
31378      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31380      * which displays a downward arrow icon).
31381      */
31382     triggerClass : 'x-form-arrow-trigger',
31383     /**
31384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31385      */
31386     shadow:'sides',
31387     /**
31388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31389      * anchor positions (defaults to 'tl-bl')
31390      */
31391     listAlign: 'tl-bl?',
31392     /**
31393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31394      */
31395     maxHeight: 300,
31396     /**
31397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31398      * query specified by the allQuery config option (defaults to 'query')
31399      */
31400     triggerAction: 'query',
31401     /**
31402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31403      * (defaults to 4, does not apply if editable = false)
31404      */
31405     minChars : 4,
31406     /**
31407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31409      */
31410     typeAhead: false,
31411     /**
31412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31414      */
31415     queryDelay: 500,
31416     /**
31417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31419      */
31420     pageSize: 0,
31421     /**
31422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31423      * when editable = true (defaults to false)
31424      */
31425     selectOnFocus:false,
31426     /**
31427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31428      */
31429     queryParam: 'query',
31430     /**
31431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31432      * when mode = 'remote' (defaults to 'Loading...')
31433      */
31434     loadingText: 'Loading...',
31435     /**
31436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31437      */
31438     resizable: false,
31439     /**
31440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31441      */
31442     handleHeight : 8,
31443     /**
31444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31445      * traditional select (defaults to true)
31446      */
31447     editable: true,
31448     /**
31449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31450      */
31451     allQuery: '',
31452     /**
31453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31454      */
31455     mode: 'remote',
31456     /**
31457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31458      * listWidth has a higher value)
31459      */
31460     minListWidth : 70,
31461     /**
31462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31463      * allow the user to set arbitrary text into the field (defaults to false)
31464      */
31465     forceSelection:false,
31466     /**
31467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31468      * if typeAhead = true (defaults to 250)
31469      */
31470     typeAheadDelay : 250,
31471     /**
31472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31474      */
31475     valueNotFoundText : undefined,
31476     
31477     /**
31478      * @cfg {String} defaultValue The value displayed after loading the store.
31479      */
31480     defaultValue: '',
31481     
31482     /**
31483      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31484      */
31485     blockFocus : false,
31486     
31487     /**
31488      * @cfg {Boolean} disableClear Disable showing of clear button.
31489      */
31490     disableClear : false,
31491     /**
31492      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31493      */
31494     alwaysQuery : false,
31495     
31496     //private
31497     addicon : false,
31498     editicon: false,
31499     
31500     // element that contains real text value.. (when hidden is used..)
31501      
31502     // private
31503     onRender : function(ct, position){
31504         Roo.form.Field.prototype.onRender.call(this, ct, position);
31505         
31506         if(this.store){
31507             this.store.on('beforeload', this.onBeforeLoad, this);
31508             this.store.on('load', this.onLoad, this);
31509             this.store.on('loadexception', this.onLoadException, this);
31510             this.store.load({});
31511         }
31512         
31513         
31514         
31515     },
31516
31517     // private
31518     initEvents : function(){
31519         //Roo.form.ComboBox.superclass.initEvents.call(this);
31520  
31521     },
31522
31523     onDestroy : function(){
31524        
31525         if(this.store){
31526             this.store.un('beforeload', this.onBeforeLoad, this);
31527             this.store.un('load', this.onLoad, this);
31528             this.store.un('loadexception', this.onLoadException, this);
31529         }
31530         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31531     },
31532
31533     // private
31534     fireKey : function(e){
31535         if(e.isNavKeyPress() && !this.list.isVisible()){
31536             this.fireEvent("specialkey", this, e);
31537         }
31538     },
31539
31540     // private
31541     onResize: function(w, h){
31542         
31543         return; 
31544     
31545         
31546     },
31547
31548     /**
31549      * Allow or prevent the user from directly editing the field text.  If false is passed,
31550      * the user will only be able to select from the items defined in the dropdown list.  This method
31551      * is the runtime equivalent of setting the 'editable' config option at config time.
31552      * @param {Boolean} value True to allow the user to directly edit the field text
31553      */
31554     setEditable : function(value){
31555          
31556     },
31557
31558     // private
31559     onBeforeLoad : function(){
31560         
31561         Roo.log("Select before load");
31562         return;
31563     
31564         this.innerList.update(this.loadingText ?
31565                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31566         //this.restrictHeight();
31567         this.selectedIndex = -1;
31568     },
31569
31570     // private
31571     onLoad : function(){
31572
31573     
31574         var dom = this.el.dom;
31575         dom.innerHTML = '';
31576          var od = dom.ownerDocument;
31577          
31578         if (this.emptyText) {
31579             var op = od.createElement('option');
31580             op.setAttribute('value', '');
31581             op.innerHTML = String.format('{0}', this.emptyText);
31582             dom.appendChild(op);
31583         }
31584         if(this.store.getCount() > 0){
31585            
31586             var vf = this.valueField;
31587             var df = this.displayField;
31588             this.store.data.each(function(r) {
31589                 // which colmsn to use... testing - cdoe / title..
31590                 var op = od.createElement('option');
31591                 op.setAttribute('value', r.data[vf]);
31592                 op.innerHTML = String.format('{0}', r.data[df]);
31593                 dom.appendChild(op);
31594             });
31595             if (typeof(this.defaultValue != 'undefined')) {
31596                 this.setValue(this.defaultValue);
31597             }
31598             
31599              
31600         }else{
31601             //this.onEmptyResults();
31602         }
31603         //this.el.focus();
31604     },
31605     // private
31606     onLoadException : function()
31607     {
31608         dom.innerHTML = '';
31609             
31610         Roo.log("Select on load exception");
31611         return;
31612     
31613         this.collapse();
31614         Roo.log(this.store.reader.jsonData);
31615         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31616             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31617         }
31618         
31619         
31620     },
31621     // private
31622     onTypeAhead : function(){
31623          
31624     },
31625
31626     // private
31627     onSelect : function(record, index){
31628         Roo.log('on select?');
31629         return;
31630         if(this.fireEvent('beforeselect', this, record, index) !== false){
31631             this.setFromData(index > -1 ? record.data : false);
31632             this.collapse();
31633             this.fireEvent('select', this, record, index);
31634         }
31635     },
31636
31637     /**
31638      * Returns the currently selected field value or empty string if no value is set.
31639      * @return {String} value The selected value
31640      */
31641     getValue : function(){
31642         var dom = this.el.dom;
31643         this.value = dom.options[dom.selectedIndex].value;
31644         return this.value;
31645         
31646     },
31647
31648     /**
31649      * Clears any text/value currently set in the field
31650      */
31651     clearValue : function(){
31652         this.value = '';
31653         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31654         
31655     },
31656
31657     /**
31658      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31659      * will be displayed in the field.  If the value does not match the data value of an existing item,
31660      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31661      * Otherwise the field will be blank (although the value will still be set).
31662      * @param {String} value The value to match
31663      */
31664     setValue : function(v){
31665         var d = this.el.dom;
31666         for (var i =0; i < d.options.length;i++) {
31667             if (v == d.options[i].value) {
31668                 d.selectedIndex = i;
31669                 this.value = v;
31670                 return;
31671             }
31672         }
31673         this.clearValue();
31674     },
31675     /**
31676      * @property {Object} the last set data for the element
31677      */
31678     
31679     lastData : false,
31680     /**
31681      * Sets the value of the field based on a object which is related to the record format for the store.
31682      * @param {Object} value the value to set as. or false on reset?
31683      */
31684     setFromData : function(o){
31685         Roo.log('setfrom data?');
31686          
31687         
31688         
31689     },
31690     // private
31691     reset : function(){
31692         this.clearValue();
31693     },
31694     // private
31695     findRecord : function(prop, value){
31696         
31697         return false;
31698     
31699         var record;
31700         if(this.store.getCount() > 0){
31701             this.store.each(function(r){
31702                 if(r.data[prop] == value){
31703                     record = r;
31704                     return false;
31705                 }
31706                 return true;
31707             });
31708         }
31709         return record;
31710     },
31711     
31712     getName: function()
31713     {
31714         // returns hidden if it's set..
31715         if (!this.rendered) {return ''};
31716         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31717         
31718     },
31719      
31720
31721     
31722
31723     // private
31724     onEmptyResults : function(){
31725         Roo.log('empty results');
31726         //this.collapse();
31727     },
31728
31729     /**
31730      * Returns true if the dropdown list is expanded, else false.
31731      */
31732     isExpanded : function(){
31733         return false;
31734     },
31735
31736     /**
31737      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31738      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31739      * @param {String} value The data value of the item to select
31740      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31741      * selected item if it is not currently in view (defaults to true)
31742      * @return {Boolean} True if the value matched an item in the list, else false
31743      */
31744     selectByValue : function(v, scrollIntoView){
31745         Roo.log('select By Value');
31746         return false;
31747     
31748         if(v !== undefined && v !== null){
31749             var r = this.findRecord(this.valueField || this.displayField, v);
31750             if(r){
31751                 this.select(this.store.indexOf(r), scrollIntoView);
31752                 return true;
31753             }
31754         }
31755         return false;
31756     },
31757
31758     /**
31759      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31760      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31761      * @param {Number} index The zero-based index of the list item to select
31762      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31763      * selected item if it is not currently in view (defaults to true)
31764      */
31765     select : function(index, scrollIntoView){
31766         Roo.log('select ');
31767         return  ;
31768         
31769         this.selectedIndex = index;
31770         this.view.select(index);
31771         if(scrollIntoView !== false){
31772             var el = this.view.getNode(index);
31773             if(el){
31774                 this.innerList.scrollChildIntoView(el, false);
31775             }
31776         }
31777     },
31778
31779       
31780
31781     // private
31782     validateBlur : function(){
31783         
31784         return;
31785         
31786     },
31787
31788     // private
31789     initQuery : function(){
31790         this.doQuery(this.getRawValue());
31791     },
31792
31793     // private
31794     doForce : function(){
31795         if(this.el.dom.value.length > 0){
31796             this.el.dom.value =
31797                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31798              
31799         }
31800     },
31801
31802     /**
31803      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31804      * query allowing the query action to be canceled if needed.
31805      * @param {String} query The SQL query to execute
31806      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31807      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31808      * saved in the current store (defaults to false)
31809      */
31810     doQuery : function(q, forceAll){
31811         
31812         Roo.log('doQuery?');
31813         if(q === undefined || q === null){
31814             q = '';
31815         }
31816         var qe = {
31817             query: q,
31818             forceAll: forceAll,
31819             combo: this,
31820             cancel:false
31821         };
31822         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31823             return false;
31824         }
31825         q = qe.query;
31826         forceAll = qe.forceAll;
31827         if(forceAll === true || (q.length >= this.minChars)){
31828             if(this.lastQuery != q || this.alwaysQuery){
31829                 this.lastQuery = q;
31830                 if(this.mode == 'local'){
31831                     this.selectedIndex = -1;
31832                     if(forceAll){
31833                         this.store.clearFilter();
31834                     }else{
31835                         this.store.filter(this.displayField, q);
31836                     }
31837                     this.onLoad();
31838                 }else{
31839                     this.store.baseParams[this.queryParam] = q;
31840                     this.store.load({
31841                         params: this.getParams(q)
31842                     });
31843                     this.expand();
31844                 }
31845             }else{
31846                 this.selectedIndex = -1;
31847                 this.onLoad();   
31848             }
31849         }
31850     },
31851
31852     // private
31853     getParams : function(q){
31854         var p = {};
31855         //p[this.queryParam] = q;
31856         if(this.pageSize){
31857             p.start = 0;
31858             p.limit = this.pageSize;
31859         }
31860         return p;
31861     },
31862
31863     /**
31864      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31865      */
31866     collapse : function(){
31867         
31868     },
31869
31870     // private
31871     collapseIf : function(e){
31872         
31873     },
31874
31875     /**
31876      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31877      */
31878     expand : function(){
31879         
31880     } ,
31881
31882     // private
31883      
31884
31885     /** 
31886     * @cfg {Boolean} grow 
31887     * @hide 
31888     */
31889     /** 
31890     * @cfg {Number} growMin 
31891     * @hide 
31892     */
31893     /** 
31894     * @cfg {Number} growMax 
31895     * @hide 
31896     */
31897     /**
31898      * @hide
31899      * @method autoSize
31900      */
31901     
31902     setWidth : function()
31903     {
31904         
31905     },
31906     getResizeEl : function(){
31907         return this.el;
31908     }
31909 });//<script type="text/javasscript">
31910  
31911
31912 /**
31913  * @class Roo.DDView
31914  * A DnD enabled version of Roo.View.
31915  * @param {Element/String} container The Element in which to create the View.
31916  * @param {String} tpl The template string used to create the markup for each element of the View
31917  * @param {Object} config The configuration properties. These include all the config options of
31918  * {@link Roo.View} plus some specific to this class.<br>
31919  * <p>
31920  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31921  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31922  * <p>
31923  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31924 .x-view-drag-insert-above {
31925         border-top:1px dotted #3366cc;
31926 }
31927 .x-view-drag-insert-below {
31928         border-bottom:1px dotted #3366cc;
31929 }
31930 </code></pre>
31931  * 
31932  */
31933  
31934 Roo.DDView = function(container, tpl, config) {
31935     Roo.DDView.superclass.constructor.apply(this, arguments);
31936     this.getEl().setStyle("outline", "0px none");
31937     this.getEl().unselectable();
31938     if (this.dragGroup) {
31939                 this.setDraggable(this.dragGroup.split(","));
31940     }
31941     if (this.dropGroup) {
31942                 this.setDroppable(this.dropGroup.split(","));
31943     }
31944     if (this.deletable) {
31945         this.setDeletable();
31946     }
31947     this.isDirtyFlag = false;
31948         this.addEvents({
31949                 "drop" : true
31950         });
31951 };
31952
31953 Roo.extend(Roo.DDView, Roo.View, {
31954 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31955 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31956 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31957 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31958
31959         isFormField: true,
31960
31961         reset: Roo.emptyFn,
31962         
31963         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31964
31965         validate: function() {
31966                 return true;
31967         },
31968         
31969         destroy: function() {
31970                 this.purgeListeners();
31971                 this.getEl.removeAllListeners();
31972                 this.getEl().remove();
31973                 if (this.dragZone) {
31974                         if (this.dragZone.destroy) {
31975                                 this.dragZone.destroy();
31976                         }
31977                 }
31978                 if (this.dropZone) {
31979                         if (this.dropZone.destroy) {
31980                                 this.dropZone.destroy();
31981                         }
31982                 }
31983         },
31984
31985 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31986         getName: function() {
31987                 return this.name;
31988         },
31989
31990 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31991         setValue: function(v) {
31992                 if (!this.store) {
31993                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31994                 }
31995                 var data = {};
31996                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31997                 this.store.proxy = new Roo.data.MemoryProxy(data);
31998                 this.store.load();
31999         },
32000
32001 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32002         getValue: function() {
32003                 var result = '(';
32004                 this.store.each(function(rec) {
32005                         result += rec.id + ',';
32006                 });
32007                 return result.substr(0, result.length - 1) + ')';
32008         },
32009         
32010         getIds: function() {
32011                 var i = 0, result = new Array(this.store.getCount());
32012                 this.store.each(function(rec) {
32013                         result[i++] = rec.id;
32014                 });
32015                 return result;
32016         },
32017         
32018         isDirty: function() {
32019                 return this.isDirtyFlag;
32020         },
32021
32022 /**
32023  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32024  *      whole Element becomes the target, and this causes the drop gesture to append.
32025  */
32026     getTargetFromEvent : function(e) {
32027                 var target = e.getTarget();
32028                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32029                 target = target.parentNode;
32030                 }
32031                 if (!target) {
32032                         target = this.el.dom.lastChild || this.el.dom;
32033                 }
32034                 return target;
32035     },
32036
32037 /**
32038  *      Create the drag data which consists of an object which has the property "ddel" as
32039  *      the drag proxy element. 
32040  */
32041     getDragData : function(e) {
32042         var target = this.findItemFromChild(e.getTarget());
32043                 if(target) {
32044                         this.handleSelection(e);
32045                         var selNodes = this.getSelectedNodes();
32046             var dragData = {
32047                 source: this,
32048                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32049                 nodes: selNodes,
32050                 records: []
32051                         };
32052                         var selectedIndices = this.getSelectedIndexes();
32053                         for (var i = 0; i < selectedIndices.length; i++) {
32054                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32055                         }
32056                         if (selNodes.length == 1) {
32057                                 dragData.ddel = target.cloneNode(true); // the div element
32058                         } else {
32059                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32060                                 div.className = 'multi-proxy';
32061                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32062                                         div.appendChild(selNodes[i].cloneNode(true));
32063                                 }
32064                                 dragData.ddel = div;
32065                         }
32066             //console.log(dragData)
32067             //console.log(dragData.ddel.innerHTML)
32068                         return dragData;
32069                 }
32070         //console.log('nodragData')
32071                 return false;
32072     },
32073     
32074 /**     Specify to which ddGroup items in this DDView may be dragged. */
32075     setDraggable: function(ddGroup) {
32076         if (ddGroup instanceof Array) {
32077                 Roo.each(ddGroup, this.setDraggable, this);
32078                 return;
32079         }
32080         if (this.dragZone) {
32081                 this.dragZone.addToGroup(ddGroup);
32082         } else {
32083                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32084                                 containerScroll: true,
32085                                 ddGroup: ddGroup 
32086
32087                         });
32088 //                      Draggability implies selection. DragZone's mousedown selects the element.
32089                         if (!this.multiSelect) { this.singleSelect = true; }
32090
32091 //                      Wire the DragZone's handlers up to methods in *this*
32092                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32093                 }
32094     },
32095
32096 /**     Specify from which ddGroup this DDView accepts drops. */
32097     setDroppable: function(ddGroup) {
32098         if (ddGroup instanceof Array) {
32099                 Roo.each(ddGroup, this.setDroppable, this);
32100                 return;
32101         }
32102         if (this.dropZone) {
32103                 this.dropZone.addToGroup(ddGroup);
32104         } else {
32105                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32106                                 containerScroll: true,
32107                                 ddGroup: ddGroup
32108                         });
32109
32110 //                      Wire the DropZone's handlers up to methods in *this*
32111                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32112                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32113                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32114                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32115                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32116                 }
32117     },
32118
32119 /**     Decide whether to drop above or below a View node. */
32120     getDropPoint : function(e, n, dd){
32121         if (n == this.el.dom) { return "above"; }
32122                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32123                 var c = t + (b - t) / 2;
32124                 var y = Roo.lib.Event.getPageY(e);
32125                 if(y <= c) {
32126                         return "above";
32127                 }else{
32128                         return "below";
32129                 }
32130     },
32131
32132     onNodeEnter : function(n, dd, e, data){
32133                 return false;
32134     },
32135     
32136     onNodeOver : function(n, dd, e, data){
32137                 var pt = this.getDropPoint(e, n, dd);
32138                 // set the insert point style on the target node
32139                 var dragElClass = this.dropNotAllowed;
32140                 if (pt) {
32141                         var targetElClass;
32142                         if (pt == "above"){
32143                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32144                                 targetElClass = "x-view-drag-insert-above";
32145                         } else {
32146                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32147                                 targetElClass = "x-view-drag-insert-below";
32148                         }
32149                         if (this.lastInsertClass != targetElClass){
32150                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32151                                 this.lastInsertClass = targetElClass;
32152                         }
32153                 }
32154                 return dragElClass;
32155         },
32156
32157     onNodeOut : function(n, dd, e, data){
32158                 this.removeDropIndicators(n);
32159     },
32160
32161     onNodeDrop : function(n, dd, e, data){
32162         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32163                 return false;
32164         }
32165         var pt = this.getDropPoint(e, n, dd);
32166                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32167                 if (pt == "below") { insertAt++; }
32168                 for (var i = 0; i < data.records.length; i++) {
32169                         var r = data.records[i];
32170                         var dup = this.store.getById(r.id);
32171                         if (dup && (dd != this.dragZone)) {
32172                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32173                         } else {
32174                                 if (data.copy) {
32175                                         this.store.insert(insertAt++, r.copy());
32176                                 } else {
32177                                         data.source.isDirtyFlag = true;
32178                                         r.store.remove(r);
32179                                         this.store.insert(insertAt++, r);
32180                                 }
32181                                 this.isDirtyFlag = true;
32182                         }
32183                 }
32184                 this.dragZone.cachedTarget = null;
32185                 return true;
32186     },
32187
32188     removeDropIndicators : function(n){
32189                 if(n){
32190                         Roo.fly(n).removeClass([
32191                                 "x-view-drag-insert-above",
32192                                 "x-view-drag-insert-below"]);
32193                         this.lastInsertClass = "_noclass";
32194                 }
32195     },
32196
32197 /**
32198  *      Utility method. Add a delete option to the DDView's context menu.
32199  *      @param {String} imageUrl The URL of the "delete" icon image.
32200  */
32201         setDeletable: function(imageUrl) {
32202                 if (!this.singleSelect && !this.multiSelect) {
32203                         this.singleSelect = true;
32204                 }
32205                 var c = this.getContextMenu();
32206                 this.contextMenu.on("itemclick", function(item) {
32207                         switch (item.id) {
32208                                 case "delete":
32209                                         this.remove(this.getSelectedIndexes());
32210                                         break;
32211                         }
32212                 }, this);
32213                 this.contextMenu.add({
32214                         icon: imageUrl,
32215                         id: "delete",
32216                         text: 'Delete'
32217                 });
32218         },
32219         
32220 /**     Return the context menu for this DDView. */
32221         getContextMenu: function() {
32222                 if (!this.contextMenu) {
32223 //                      Create the View's context menu
32224                         this.contextMenu = new Roo.menu.Menu({
32225                                 id: this.id + "-contextmenu"
32226                         });
32227                         this.el.on("contextmenu", this.showContextMenu, this);
32228                 }
32229                 return this.contextMenu;
32230         },
32231         
32232         disableContextMenu: function() {
32233                 if (this.contextMenu) {
32234                         this.el.un("contextmenu", this.showContextMenu, this);
32235                 }
32236         },
32237
32238         showContextMenu: function(e, item) {
32239         item = this.findItemFromChild(e.getTarget());
32240                 if (item) {
32241                         e.stopEvent();
32242                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32243                         this.contextMenu.showAt(e.getXY());
32244             }
32245     },
32246
32247 /**
32248  *      Remove {@link Roo.data.Record}s at the specified indices.
32249  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32250  */
32251     remove: function(selectedIndices) {
32252                 selectedIndices = [].concat(selectedIndices);
32253                 for (var i = 0; i < selectedIndices.length; i++) {
32254                         var rec = this.store.getAt(selectedIndices[i]);
32255                         this.store.remove(rec);
32256                 }
32257     },
32258
32259 /**
32260  *      Double click fires the event, but also, if this is draggable, and there is only one other
32261  *      related DropZone, it transfers the selected node.
32262  */
32263     onDblClick : function(e){
32264         var item = this.findItemFromChild(e.getTarget());
32265         if(item){
32266             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32267                 return false;
32268             }
32269             if (this.dragGroup) {
32270                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32271                     while (targets.indexOf(this.dropZone) > -1) {
32272                             targets.remove(this.dropZone);
32273                                 }
32274                     if (targets.length == 1) {
32275                                         this.dragZone.cachedTarget = null;
32276                         var el = Roo.get(targets[0].getEl());
32277                         var box = el.getBox(true);
32278                         targets[0].onNodeDrop(el.dom, {
32279                                 target: el.dom,
32280                                 xy: [box.x, box.y + box.height - 1]
32281                         }, null, this.getDragData(e));
32282                     }
32283                 }
32284         }
32285     },
32286     
32287     handleSelection: function(e) {
32288                 this.dragZone.cachedTarget = null;
32289         var item = this.findItemFromChild(e.getTarget());
32290         if (!item) {
32291                 this.clearSelections(true);
32292                 return;
32293         }
32294                 if (item && (this.multiSelect || this.singleSelect)){
32295                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32296                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32297                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32298                                 this.unselect(item);
32299                         } else {
32300                                 this.select(item, this.multiSelect && e.ctrlKey);
32301                                 this.lastSelection = item;
32302                         }
32303                 }
32304     },
32305
32306     onItemClick : function(item, index, e){
32307                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32308                         return false;
32309                 }
32310                 return true;
32311     },
32312
32313     unselect : function(nodeInfo, suppressEvent){
32314                 var node = this.getNode(nodeInfo);
32315                 if(node && this.isSelected(node)){
32316                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32317                                 Roo.fly(node).removeClass(this.selectedClass);
32318                                 this.selections.remove(node);
32319                                 if(!suppressEvent){
32320                                         this.fireEvent("selectionchange", this, this.selections);
32321                                 }
32322                         }
32323                 }
32324     }
32325 });
32326 /*
32327  * Based on:
32328  * Ext JS Library 1.1.1
32329  * Copyright(c) 2006-2007, Ext JS, LLC.
32330  *
32331  * Originally Released Under LGPL - original licence link has changed is not relivant.
32332  *
32333  * Fork - LGPL
32334  * <script type="text/javascript">
32335  */
32336  
32337 /**
32338  * @class Roo.LayoutManager
32339  * @extends Roo.util.Observable
32340  * Base class for layout managers.
32341  */
32342 Roo.LayoutManager = function(container, config){
32343     Roo.LayoutManager.superclass.constructor.call(this);
32344     this.el = Roo.get(container);
32345     // ie scrollbar fix
32346     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32347         document.body.scroll = "no";
32348     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32349         this.el.position('relative');
32350     }
32351     this.id = this.el.id;
32352     this.el.addClass("x-layout-container");
32353     /** false to disable window resize monitoring @type Boolean */
32354     this.monitorWindowResize = true;
32355     this.regions = {};
32356     this.addEvents({
32357         /**
32358          * @event layout
32359          * Fires when a layout is performed. 
32360          * @param {Roo.LayoutManager} this
32361          */
32362         "layout" : true,
32363         /**
32364          * @event regionresized
32365          * Fires when the user resizes a region. 
32366          * @param {Roo.LayoutRegion} region The resized region
32367          * @param {Number} newSize The new size (width for east/west, height for north/south)
32368          */
32369         "regionresized" : true,
32370         /**
32371          * @event regioncollapsed
32372          * Fires when a region is collapsed. 
32373          * @param {Roo.LayoutRegion} region The collapsed region
32374          */
32375         "regioncollapsed" : true,
32376         /**
32377          * @event regionexpanded
32378          * Fires when a region is expanded.  
32379          * @param {Roo.LayoutRegion} region The expanded region
32380          */
32381         "regionexpanded" : true
32382     });
32383     this.updating = false;
32384     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32385 };
32386
32387 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32388     /**
32389      * Returns true if this layout is currently being updated
32390      * @return {Boolean}
32391      */
32392     isUpdating : function(){
32393         return this.updating; 
32394     },
32395     
32396     /**
32397      * Suspend the LayoutManager from doing auto-layouts while
32398      * making multiple add or remove calls
32399      */
32400     beginUpdate : function(){
32401         this.updating = true;    
32402     },
32403     
32404     /**
32405      * Restore auto-layouts and optionally disable the manager from performing a layout
32406      * @param {Boolean} noLayout true to disable a layout update 
32407      */
32408     endUpdate : function(noLayout){
32409         this.updating = false;
32410         if(!noLayout){
32411             this.layout();
32412         }    
32413     },
32414     
32415     layout: function(){
32416         
32417     },
32418     
32419     onRegionResized : function(region, newSize){
32420         this.fireEvent("regionresized", region, newSize);
32421         this.layout();
32422     },
32423     
32424     onRegionCollapsed : function(region){
32425         this.fireEvent("regioncollapsed", region);
32426     },
32427     
32428     onRegionExpanded : function(region){
32429         this.fireEvent("regionexpanded", region);
32430     },
32431         
32432     /**
32433      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32434      * performs box-model adjustments.
32435      * @return {Object} The size as an object {width: (the width), height: (the height)}
32436      */
32437     getViewSize : function(){
32438         var size;
32439         if(this.el.dom != document.body){
32440             size = this.el.getSize();
32441         }else{
32442             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32443         }
32444         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32445         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32446         return size;
32447     },
32448     
32449     /**
32450      * Returns the Element this layout is bound to.
32451      * @return {Roo.Element}
32452      */
32453     getEl : function(){
32454         return this.el;
32455     },
32456     
32457     /**
32458      * Returns the specified region.
32459      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32460      * @return {Roo.LayoutRegion}
32461      */
32462     getRegion : function(target){
32463         return this.regions[target.toLowerCase()];
32464     },
32465     
32466     onWindowResize : function(){
32467         if(this.monitorWindowResize){
32468             this.layout();
32469         }
32470     }
32471 });/*
32472  * Based on:
32473  * Ext JS Library 1.1.1
32474  * Copyright(c) 2006-2007, Ext JS, LLC.
32475  *
32476  * Originally Released Under LGPL - original licence link has changed is not relivant.
32477  *
32478  * Fork - LGPL
32479  * <script type="text/javascript">
32480  */
32481 /**
32482  * @class Roo.BorderLayout
32483  * @extends Roo.LayoutManager
32484  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32485  * please see: <br><br>
32486  * <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>
32487  * <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>
32488  * Example:
32489  <pre><code>
32490  var layout = new Roo.BorderLayout(document.body, {
32491     north: {
32492         initialSize: 25,
32493         titlebar: false
32494     },
32495     west: {
32496         split:true,
32497         initialSize: 200,
32498         minSize: 175,
32499         maxSize: 400,
32500         titlebar: true,
32501         collapsible: true
32502     },
32503     east: {
32504         split:true,
32505         initialSize: 202,
32506         minSize: 175,
32507         maxSize: 400,
32508         titlebar: true,
32509         collapsible: true
32510     },
32511     south: {
32512         split:true,
32513         initialSize: 100,
32514         minSize: 100,
32515         maxSize: 200,
32516         titlebar: true,
32517         collapsible: true
32518     },
32519     center: {
32520         titlebar: true,
32521         autoScroll:true,
32522         resizeTabs: true,
32523         minTabWidth: 50,
32524         preferredTabWidth: 150
32525     }
32526 });
32527
32528 // shorthand
32529 var CP = Roo.ContentPanel;
32530
32531 layout.beginUpdate();
32532 layout.add("north", new CP("north", "North"));
32533 layout.add("south", new CP("south", {title: "South", closable: true}));
32534 layout.add("west", new CP("west", {title: "West"}));
32535 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32536 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32537 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32538 layout.getRegion("center").showPanel("center1");
32539 layout.endUpdate();
32540 </code></pre>
32541
32542 <b>The container the layout is rendered into can be either the body element or any other element.
32543 If it is not the body element, the container needs to either be an absolute positioned element,
32544 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32545 the container size if it is not the body element.</b>
32546
32547 * @constructor
32548 * Create a new BorderLayout
32549 * @param {String/HTMLElement/Element} container The container this layout is bound to
32550 * @param {Object} config Configuration options
32551  */
32552 Roo.BorderLayout = function(container, config){
32553     config = config || {};
32554     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32555     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32556     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32557         var target = this.factory.validRegions[i];
32558         if(config[target]){
32559             this.addRegion(target, config[target]);
32560         }
32561     }
32562 };
32563
32564 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32565     /**
32566      * Creates and adds a new region if it doesn't already exist.
32567      * @param {String} target The target region key (north, south, east, west or center).
32568      * @param {Object} config The regions config object
32569      * @return {BorderLayoutRegion} The new region
32570      */
32571     addRegion : function(target, config){
32572         if(!this.regions[target]){
32573             var r = this.factory.create(target, this, config);
32574             this.bindRegion(target, r);
32575         }
32576         return this.regions[target];
32577     },
32578
32579     // private (kinda)
32580     bindRegion : function(name, r){
32581         this.regions[name] = r;
32582         r.on("visibilitychange", this.layout, this);
32583         r.on("paneladded", this.layout, this);
32584         r.on("panelremoved", this.layout, this);
32585         r.on("invalidated", this.layout, this);
32586         r.on("resized", this.onRegionResized, this);
32587         r.on("collapsed", this.onRegionCollapsed, this);
32588         r.on("expanded", this.onRegionExpanded, this);
32589     },
32590
32591     /**
32592      * Performs a layout update.
32593      */
32594     layout : function(){
32595         if(this.updating) return;
32596         var size = this.getViewSize();
32597         var w = size.width;
32598         var h = size.height;
32599         var centerW = w;
32600         var centerH = h;
32601         var centerY = 0;
32602         var centerX = 0;
32603         //var x = 0, y = 0;
32604
32605         var rs = this.regions;
32606         var north = rs["north"];
32607         var south = rs["south"]; 
32608         var west = rs["west"];
32609         var east = rs["east"];
32610         var center = rs["center"];
32611         //if(this.hideOnLayout){ // not supported anymore
32612             //c.el.setStyle("display", "none");
32613         //}
32614         if(north && north.isVisible()){
32615             var b = north.getBox();
32616             var m = north.getMargins();
32617             b.width = w - (m.left+m.right);
32618             b.x = m.left;
32619             b.y = m.top;
32620             centerY = b.height + b.y + m.bottom;
32621             centerH -= centerY;
32622             north.updateBox(this.safeBox(b));
32623         }
32624         if(south && south.isVisible()){
32625             var b = south.getBox();
32626             var m = south.getMargins();
32627             b.width = w - (m.left+m.right);
32628             b.x = m.left;
32629             var totalHeight = (b.height + m.top + m.bottom);
32630             b.y = h - totalHeight + m.top;
32631             centerH -= totalHeight;
32632             south.updateBox(this.safeBox(b));
32633         }
32634         if(west && west.isVisible()){
32635             var b = west.getBox();
32636             var m = west.getMargins();
32637             b.height = centerH - (m.top+m.bottom);
32638             b.x = m.left;
32639             b.y = centerY + m.top;
32640             var totalWidth = (b.width + m.left + m.right);
32641             centerX += totalWidth;
32642             centerW -= totalWidth;
32643             west.updateBox(this.safeBox(b));
32644         }
32645         if(east && east.isVisible()){
32646             var b = east.getBox();
32647             var m = east.getMargins();
32648             b.height = centerH - (m.top+m.bottom);
32649             var totalWidth = (b.width + m.left + m.right);
32650             b.x = w - totalWidth + m.left;
32651             b.y = centerY + m.top;
32652             centerW -= totalWidth;
32653             east.updateBox(this.safeBox(b));
32654         }
32655         if(center){
32656             var m = center.getMargins();
32657             var centerBox = {
32658                 x: centerX + m.left,
32659                 y: centerY + m.top,
32660                 width: centerW - (m.left+m.right),
32661                 height: centerH - (m.top+m.bottom)
32662             };
32663             //if(this.hideOnLayout){
32664                 //center.el.setStyle("display", "block");
32665             //}
32666             center.updateBox(this.safeBox(centerBox));
32667         }
32668         this.el.repaint();
32669         this.fireEvent("layout", this);
32670     },
32671
32672     // private
32673     safeBox : function(box){
32674         box.width = Math.max(0, box.width);
32675         box.height = Math.max(0, box.height);
32676         return box;
32677     },
32678
32679     /**
32680      * Adds a ContentPanel (or subclass) to this layout.
32681      * @param {String} target The target region key (north, south, east, west or center).
32682      * @param {Roo.ContentPanel} panel The panel to add
32683      * @return {Roo.ContentPanel} The added panel
32684      */
32685     add : function(target, panel){
32686          
32687         target = target.toLowerCase();
32688         return this.regions[target].add(panel);
32689     },
32690
32691     /**
32692      * Remove a ContentPanel (or subclass) to this layout.
32693      * @param {String} target The target region key (north, south, east, west or center).
32694      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32695      * @return {Roo.ContentPanel} The removed panel
32696      */
32697     remove : function(target, panel){
32698         target = target.toLowerCase();
32699         return this.regions[target].remove(panel);
32700     },
32701
32702     /**
32703      * Searches all regions for a panel with the specified id
32704      * @param {String} panelId
32705      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32706      */
32707     findPanel : function(panelId){
32708         var rs = this.regions;
32709         for(var target in rs){
32710             if(typeof rs[target] != "function"){
32711                 var p = rs[target].getPanel(panelId);
32712                 if(p){
32713                     return p;
32714                 }
32715             }
32716         }
32717         return null;
32718     },
32719
32720     /**
32721      * Searches all regions for a panel with the specified id and activates (shows) it.
32722      * @param {String/ContentPanel} panelId The panels id or the panel itself
32723      * @return {Roo.ContentPanel} The shown panel or null
32724      */
32725     showPanel : function(panelId) {
32726       var rs = this.regions;
32727       for(var target in rs){
32728          var r = rs[target];
32729          if(typeof r != "function"){
32730             if(r.hasPanel(panelId)){
32731                return r.showPanel(panelId);
32732             }
32733          }
32734       }
32735       return null;
32736    },
32737
32738    /**
32739      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32740      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32741      */
32742     restoreState : function(provider){
32743         if(!provider){
32744             provider = Roo.state.Manager;
32745         }
32746         var sm = new Roo.LayoutStateManager();
32747         sm.init(this, provider);
32748     },
32749
32750     /**
32751      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32752      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32753      * a valid ContentPanel config object.  Example:
32754      * <pre><code>
32755 // Create the main layout
32756 var layout = new Roo.BorderLayout('main-ct', {
32757     west: {
32758         split:true,
32759         minSize: 175,
32760         titlebar: true
32761     },
32762     center: {
32763         title:'Components'
32764     }
32765 }, 'main-ct');
32766
32767 // Create and add multiple ContentPanels at once via configs
32768 layout.batchAdd({
32769    west: {
32770        id: 'source-files',
32771        autoCreate:true,
32772        title:'Ext Source Files',
32773        autoScroll:true,
32774        fitToFrame:true
32775    },
32776    center : {
32777        el: cview,
32778        autoScroll:true,
32779        fitToFrame:true,
32780        toolbar: tb,
32781        resizeEl:'cbody'
32782    }
32783 });
32784 </code></pre>
32785      * @param {Object} regions An object containing ContentPanel configs by region name
32786      */
32787     batchAdd : function(regions){
32788         this.beginUpdate();
32789         for(var rname in regions){
32790             var lr = this.regions[rname];
32791             if(lr){
32792                 this.addTypedPanels(lr, regions[rname]);
32793             }
32794         }
32795         this.endUpdate();
32796     },
32797
32798     // private
32799     addTypedPanels : function(lr, ps){
32800         if(typeof ps == 'string'){
32801             lr.add(new Roo.ContentPanel(ps));
32802         }
32803         else if(ps instanceof Array){
32804             for(var i =0, len = ps.length; i < len; i++){
32805                 this.addTypedPanels(lr, ps[i]);
32806             }
32807         }
32808         else if(!ps.events){ // raw config?
32809             var el = ps.el;
32810             delete ps.el; // prevent conflict
32811             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32812         }
32813         else {  // panel object assumed!
32814             lr.add(ps);
32815         }
32816     },
32817     /**
32818      * Adds a xtype elements to the layout.
32819      * <pre><code>
32820
32821 layout.addxtype({
32822        xtype : 'ContentPanel',
32823        region: 'west',
32824        items: [ .... ]
32825    }
32826 );
32827
32828 layout.addxtype({
32829         xtype : 'NestedLayoutPanel',
32830         region: 'west',
32831         layout: {
32832            center: { },
32833            west: { }   
32834         },
32835         items : [ ... list of content panels or nested layout panels.. ]
32836    }
32837 );
32838 </code></pre>
32839      * @param {Object} cfg Xtype definition of item to add.
32840      */
32841     addxtype : function(cfg)
32842     {
32843         // basically accepts a pannel...
32844         // can accept a layout region..!?!?
32845         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32846         
32847         if (!cfg.xtype.match(/Panel$/)) {
32848             return false;
32849         }
32850         var ret = false;
32851         
32852         if (typeof(cfg.region) == 'undefined') {
32853             Roo.log("Failed to add Panel, region was not set");
32854             Roo.log(cfg);
32855             return false;
32856         }
32857         var region = cfg.region;
32858         delete cfg.region;
32859         
32860           
32861         var xitems = [];
32862         if (cfg.items) {
32863             xitems = cfg.items;
32864             delete cfg.items;
32865         }
32866         var nb = false;
32867         
32868         switch(cfg.xtype) 
32869         {
32870             case 'ContentPanel':  // ContentPanel (el, cfg)
32871             case 'ScrollPanel':  // ContentPanel (el, cfg)
32872             case 'ViewPanel': 
32873                 if(cfg.autoCreate) {
32874                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32875                 } else {
32876                     var el = this.el.createChild();
32877                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32878                 }
32879                 
32880                 this.add(region, ret);
32881                 break;
32882             
32883             
32884             case 'TreePanel': // our new panel!
32885                 cfg.el = this.el.createChild();
32886                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32887                 this.add(region, ret);
32888                 break;
32889             
32890             case 'NestedLayoutPanel': 
32891                 // create a new Layout (which is  a Border Layout...
32892                 var el = this.el.createChild();
32893                 var clayout = cfg.layout;
32894                 delete cfg.layout;
32895                 clayout.items   = clayout.items  || [];
32896                 // replace this exitems with the clayout ones..
32897                 xitems = clayout.items;
32898                  
32899                 
32900                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32901                     cfg.background = false;
32902                 }
32903                 var layout = new Roo.BorderLayout(el, clayout);
32904                 
32905                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32906                 //console.log('adding nested layout panel '  + cfg.toSource());
32907                 this.add(region, ret);
32908                 nb = {}; /// find first...
32909                 break;
32910                 
32911             case 'GridPanel': 
32912             
32913                 // needs grid and region
32914                 
32915                 //var el = this.getRegion(region).el.createChild();
32916                 var el = this.el.createChild();
32917                 // create the grid first...
32918                 
32919                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32920                 delete cfg.grid;
32921                 if (region == 'center' && this.active ) {
32922                     cfg.background = false;
32923                 }
32924                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32925                 
32926                 this.add(region, ret);
32927                 if (cfg.background) {
32928                     ret.on('activate', function(gp) {
32929                         if (!gp.grid.rendered) {
32930                             gp.grid.render();
32931                         }
32932                     });
32933                 } else {
32934                     grid.render();
32935                 }
32936                 break;
32937            
32938            
32939            
32940                 
32941                 
32942                 
32943             default:
32944                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32945                     
32946                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32947                     this.add(region, ret);
32948                 } else {
32949                 
32950                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32951                     return null;
32952                 }
32953                 
32954              // GridPanel (grid, cfg)
32955             
32956         }
32957         this.beginUpdate();
32958         // add children..
32959         var region = '';
32960         var abn = {};
32961         Roo.each(xitems, function(i)  {
32962             region = nb && i.region ? i.region : false;
32963             
32964             var add = ret.addxtype(i);
32965            
32966             if (region) {
32967                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32968                 if (!i.background) {
32969                     abn[region] = nb[region] ;
32970                 }
32971             }
32972             
32973         });
32974         this.endUpdate();
32975
32976         // make the last non-background panel active..
32977         //if (nb) { Roo.log(abn); }
32978         if (nb) {
32979             
32980             for(var r in abn) {
32981                 region = this.getRegion(r);
32982                 if (region) {
32983                     // tried using nb[r], but it does not work..
32984                      
32985                     region.showPanel(abn[r]);
32986                    
32987                 }
32988             }
32989         }
32990         return ret;
32991         
32992     }
32993 });
32994
32995 /**
32996  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32997  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32998  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32999  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33000  * <pre><code>
33001 // shorthand
33002 var CP = Roo.ContentPanel;
33003
33004 var layout = Roo.BorderLayout.create({
33005     north: {
33006         initialSize: 25,
33007         titlebar: false,
33008         panels: [new CP("north", "North")]
33009     },
33010     west: {
33011         split:true,
33012         initialSize: 200,
33013         minSize: 175,
33014         maxSize: 400,
33015         titlebar: true,
33016         collapsible: true,
33017         panels: [new CP("west", {title: "West"})]
33018     },
33019     east: {
33020         split:true,
33021         initialSize: 202,
33022         minSize: 175,
33023         maxSize: 400,
33024         titlebar: true,
33025         collapsible: true,
33026         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33027     },
33028     south: {
33029         split:true,
33030         initialSize: 100,
33031         minSize: 100,
33032         maxSize: 200,
33033         titlebar: true,
33034         collapsible: true,
33035         panels: [new CP("south", {title: "South", closable: true})]
33036     },
33037     center: {
33038         titlebar: true,
33039         autoScroll:true,
33040         resizeTabs: true,
33041         minTabWidth: 50,
33042         preferredTabWidth: 150,
33043         panels: [
33044             new CP("center1", {title: "Close Me", closable: true}),
33045             new CP("center2", {title: "Center Panel", closable: false})
33046         ]
33047     }
33048 }, document.body);
33049
33050 layout.getRegion("center").showPanel("center1");
33051 </code></pre>
33052  * @param config
33053  * @param targetEl
33054  */
33055 Roo.BorderLayout.create = function(config, targetEl){
33056     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33057     layout.beginUpdate();
33058     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33059     for(var j = 0, jlen = regions.length; j < jlen; j++){
33060         var lr = regions[j];
33061         if(layout.regions[lr] && config[lr].panels){
33062             var r = layout.regions[lr];
33063             var ps = config[lr].panels;
33064             layout.addTypedPanels(r, ps);
33065         }
33066     }
33067     layout.endUpdate();
33068     return layout;
33069 };
33070
33071 // private
33072 Roo.BorderLayout.RegionFactory = {
33073     // private
33074     validRegions : ["north","south","east","west","center"],
33075
33076     // private
33077     create : function(target, mgr, config){
33078         target = target.toLowerCase();
33079         if(config.lightweight || config.basic){
33080             return new Roo.BasicLayoutRegion(mgr, config, target);
33081         }
33082         switch(target){
33083             case "north":
33084                 return new Roo.NorthLayoutRegion(mgr, config);
33085             case "south":
33086                 return new Roo.SouthLayoutRegion(mgr, config);
33087             case "east":
33088                 return new Roo.EastLayoutRegion(mgr, config);
33089             case "west":
33090                 return new Roo.WestLayoutRegion(mgr, config);
33091             case "center":
33092                 return new Roo.CenterLayoutRegion(mgr, config);
33093         }
33094         throw 'Layout region "'+target+'" not supported.';
33095     }
33096 };/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106  
33107 /**
33108  * @class Roo.BasicLayoutRegion
33109  * @extends Roo.util.Observable
33110  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33111  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33112  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33113  */
33114 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33115     this.mgr = mgr;
33116     this.position  = pos;
33117     this.events = {
33118         /**
33119          * @scope Roo.BasicLayoutRegion
33120          */
33121         
33122         /**
33123          * @event beforeremove
33124          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33125          * @param {Roo.LayoutRegion} this
33126          * @param {Roo.ContentPanel} panel The panel
33127          * @param {Object} e The cancel event object
33128          */
33129         "beforeremove" : true,
33130         /**
33131          * @event invalidated
33132          * Fires when the layout for this region is changed.
33133          * @param {Roo.LayoutRegion} this
33134          */
33135         "invalidated" : true,
33136         /**
33137          * @event visibilitychange
33138          * Fires when this region is shown or hidden 
33139          * @param {Roo.LayoutRegion} this
33140          * @param {Boolean} visibility true or false
33141          */
33142         "visibilitychange" : true,
33143         /**
33144          * @event paneladded
33145          * Fires when a panel is added. 
33146          * @param {Roo.LayoutRegion} this
33147          * @param {Roo.ContentPanel} panel The panel
33148          */
33149         "paneladded" : true,
33150         /**
33151          * @event panelremoved
33152          * Fires when a panel is removed. 
33153          * @param {Roo.LayoutRegion} this
33154          * @param {Roo.ContentPanel} panel The panel
33155          */
33156         "panelremoved" : true,
33157         /**
33158          * @event collapsed
33159          * Fires when this region is collapsed.
33160          * @param {Roo.LayoutRegion} this
33161          */
33162         "collapsed" : true,
33163         /**
33164          * @event expanded
33165          * Fires when this region is expanded.
33166          * @param {Roo.LayoutRegion} this
33167          */
33168         "expanded" : true,
33169         /**
33170          * @event slideshow
33171          * Fires when this region is slid into view.
33172          * @param {Roo.LayoutRegion} this
33173          */
33174         "slideshow" : true,
33175         /**
33176          * @event slidehide
33177          * Fires when this region slides out of view. 
33178          * @param {Roo.LayoutRegion} this
33179          */
33180         "slidehide" : true,
33181         /**
33182          * @event panelactivated
33183          * Fires when a panel is activated. 
33184          * @param {Roo.LayoutRegion} this
33185          * @param {Roo.ContentPanel} panel The activated panel
33186          */
33187         "panelactivated" : true,
33188         /**
33189          * @event resized
33190          * Fires when the user resizes this region. 
33191          * @param {Roo.LayoutRegion} this
33192          * @param {Number} newSize The new size (width for east/west, height for north/south)
33193          */
33194         "resized" : true
33195     };
33196     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33197     this.panels = new Roo.util.MixedCollection();
33198     this.panels.getKey = this.getPanelId.createDelegate(this);
33199     this.box = null;
33200     this.activePanel = null;
33201     // ensure listeners are added...
33202     
33203     if (config.listeners || config.events) {
33204         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33205             listeners : config.listeners || {},
33206             events : config.events || {}
33207         });
33208     }
33209     
33210     if(skipConfig !== true){
33211         this.applyConfig(config);
33212     }
33213 };
33214
33215 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33216     getPanelId : function(p){
33217         return p.getId();
33218     },
33219     
33220     applyConfig : function(config){
33221         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33222         this.config = config;
33223         
33224     },
33225     
33226     /**
33227      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33228      * the width, for horizontal (north, south) the height.
33229      * @param {Number} newSize The new width or height
33230      */
33231     resizeTo : function(newSize){
33232         var el = this.el ? this.el :
33233                  (this.activePanel ? this.activePanel.getEl() : null);
33234         if(el){
33235             switch(this.position){
33236                 case "east":
33237                 case "west":
33238                     el.setWidth(newSize);
33239                     this.fireEvent("resized", this, newSize);
33240                 break;
33241                 case "north":
33242                 case "south":
33243                     el.setHeight(newSize);
33244                     this.fireEvent("resized", this, newSize);
33245                 break;                
33246             }
33247         }
33248     },
33249     
33250     getBox : function(){
33251         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33252     },
33253     
33254     getMargins : function(){
33255         return this.margins;
33256     },
33257     
33258     updateBox : function(box){
33259         this.box = box;
33260         var el = this.activePanel.getEl();
33261         el.dom.style.left = box.x + "px";
33262         el.dom.style.top = box.y + "px";
33263         this.activePanel.setSize(box.width, box.height);
33264     },
33265     
33266     /**
33267      * Returns the container element for this region.
33268      * @return {Roo.Element}
33269      */
33270     getEl : function(){
33271         return this.activePanel;
33272     },
33273     
33274     /**
33275      * Returns true if this region is currently visible.
33276      * @return {Boolean}
33277      */
33278     isVisible : function(){
33279         return this.activePanel ? true : false;
33280     },
33281     
33282     setActivePanel : function(panel){
33283         panel = this.getPanel(panel);
33284         if(this.activePanel && this.activePanel != panel){
33285             this.activePanel.setActiveState(false);
33286             this.activePanel.getEl().setLeftTop(-10000,-10000);
33287         }
33288         this.activePanel = panel;
33289         panel.setActiveState(true);
33290         if(this.box){
33291             panel.setSize(this.box.width, this.box.height);
33292         }
33293         this.fireEvent("panelactivated", this, panel);
33294         this.fireEvent("invalidated");
33295     },
33296     
33297     /**
33298      * Show the specified panel.
33299      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33300      * @return {Roo.ContentPanel} The shown panel or null
33301      */
33302     showPanel : function(panel){
33303         if(panel = this.getPanel(panel)){
33304             this.setActivePanel(panel);
33305         }
33306         return panel;
33307     },
33308     
33309     /**
33310      * Get the active panel for this region.
33311      * @return {Roo.ContentPanel} The active panel or null
33312      */
33313     getActivePanel : function(){
33314         return this.activePanel;
33315     },
33316     
33317     /**
33318      * Add the passed ContentPanel(s)
33319      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33320      * @return {Roo.ContentPanel} The panel added (if only one was added)
33321      */
33322     add : function(panel){
33323         if(arguments.length > 1){
33324             for(var i = 0, len = arguments.length; i < len; i++) {
33325                 this.add(arguments[i]);
33326             }
33327             return null;
33328         }
33329         if(this.hasPanel(panel)){
33330             this.showPanel(panel);
33331             return panel;
33332         }
33333         var el = panel.getEl();
33334         if(el.dom.parentNode != this.mgr.el.dom){
33335             this.mgr.el.dom.appendChild(el.dom);
33336         }
33337         if(panel.setRegion){
33338             panel.setRegion(this);
33339         }
33340         this.panels.add(panel);
33341         el.setStyle("position", "absolute");
33342         if(!panel.background){
33343             this.setActivePanel(panel);
33344             if(this.config.initialSize && this.panels.getCount()==1){
33345                 this.resizeTo(this.config.initialSize);
33346             }
33347         }
33348         this.fireEvent("paneladded", this, panel);
33349         return panel;
33350     },
33351     
33352     /**
33353      * Returns true if the panel is in this region.
33354      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33355      * @return {Boolean}
33356      */
33357     hasPanel : function(panel){
33358         if(typeof panel == "object"){ // must be panel obj
33359             panel = panel.getId();
33360         }
33361         return this.getPanel(panel) ? true : false;
33362     },
33363     
33364     /**
33365      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33366      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33367      * @param {Boolean} preservePanel Overrides the config preservePanel option
33368      * @return {Roo.ContentPanel} The panel that was removed
33369      */
33370     remove : function(panel, preservePanel){
33371         panel = this.getPanel(panel);
33372         if(!panel){
33373             return null;
33374         }
33375         var e = {};
33376         this.fireEvent("beforeremove", this, panel, e);
33377         if(e.cancel === true){
33378             return null;
33379         }
33380         var panelId = panel.getId();
33381         this.panels.removeKey(panelId);
33382         return panel;
33383     },
33384     
33385     /**
33386      * Returns the panel specified or null if it's not in this region.
33387      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33388      * @return {Roo.ContentPanel}
33389      */
33390     getPanel : function(id){
33391         if(typeof id == "object"){ // must be panel obj
33392             return id;
33393         }
33394         return this.panels.get(id);
33395     },
33396     
33397     /**
33398      * Returns this regions position (north/south/east/west/center).
33399      * @return {String} 
33400      */
33401     getPosition: function(){
33402         return this.position;    
33403     }
33404 });/*
33405  * Based on:
33406  * Ext JS Library 1.1.1
33407  * Copyright(c) 2006-2007, Ext JS, LLC.
33408  *
33409  * Originally Released Under LGPL - original licence link has changed is not relivant.
33410  *
33411  * Fork - LGPL
33412  * <script type="text/javascript">
33413  */
33414  
33415 /**
33416  * @class Roo.LayoutRegion
33417  * @extends Roo.BasicLayoutRegion
33418  * This class represents a region in a layout manager.
33419  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33420  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33421  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33422  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33423  * @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})
33424  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33425  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33426  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33427  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33428  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33429  * @cfg {String}    title           The title for the region (overrides panel titles)
33430  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33431  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33432  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33433  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33434  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33435  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33436  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33437  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33438  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33439  * @cfg {Boolean}   showPin         True to show a pin button
33440  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33441  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33442  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33443  * @cfg {Number}    width           For East/West panels
33444  * @cfg {Number}    height          For North/South panels
33445  * @cfg {Boolean}   split           To show the splitter
33446  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33447  */
33448 Roo.LayoutRegion = function(mgr, config, pos){
33449     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33450     var dh = Roo.DomHelper;
33451     /** This region's container element 
33452     * @type Roo.Element */
33453     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33454     /** This region's title element 
33455     * @type Roo.Element */
33456
33457     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33458         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33459         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33460     ]}, true);
33461     this.titleEl.enableDisplayMode();
33462     /** This region's title text element 
33463     * @type HTMLElement */
33464     this.titleTextEl = this.titleEl.dom.firstChild;
33465     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33466     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33467     this.closeBtn.enableDisplayMode();
33468     this.closeBtn.on("click", this.closeClicked, this);
33469     this.closeBtn.hide();
33470
33471     this.createBody(config);
33472     this.visible = true;
33473     this.collapsed = false;
33474
33475     if(config.hideWhenEmpty){
33476         this.hide();
33477         this.on("paneladded", this.validateVisibility, this);
33478         this.on("panelremoved", this.validateVisibility, this);
33479     }
33480     this.applyConfig(config);
33481 };
33482
33483 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33484
33485     createBody : function(){
33486         /** This region's body element 
33487         * @type Roo.Element */
33488         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33489     },
33490
33491     applyConfig : function(c){
33492         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33493             var dh = Roo.DomHelper;
33494             if(c.titlebar !== false){
33495                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33496                 this.collapseBtn.on("click", this.collapse, this);
33497                 this.collapseBtn.enableDisplayMode();
33498
33499                 if(c.showPin === true || this.showPin){
33500                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33501                     this.stickBtn.enableDisplayMode();
33502                     this.stickBtn.on("click", this.expand, this);
33503                     this.stickBtn.hide();
33504                 }
33505             }
33506             /** This region's collapsed element
33507             * @type Roo.Element */
33508             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33509                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33510             ]}, true);
33511             if(c.floatable !== false){
33512                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33513                this.collapsedEl.on("click", this.collapseClick, this);
33514             }
33515
33516             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33517                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33518                    id: "message", unselectable: "on", style:{"float":"left"}});
33519                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33520              }
33521             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33522             this.expandBtn.on("click", this.expand, this);
33523         }
33524         if(this.collapseBtn){
33525             this.collapseBtn.setVisible(c.collapsible == true);
33526         }
33527         this.cmargins = c.cmargins || this.cmargins ||
33528                          (this.position == "west" || this.position == "east" ?
33529                              {top: 0, left: 2, right:2, bottom: 0} :
33530                              {top: 2, left: 0, right:0, bottom: 2});
33531         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33532         this.bottomTabs = c.tabPosition != "top";
33533         this.autoScroll = c.autoScroll || false;
33534         if(this.autoScroll){
33535             this.bodyEl.setStyle("overflow", "auto");
33536         }else{
33537             this.bodyEl.setStyle("overflow", "hidden");
33538         }
33539         //if(c.titlebar !== false){
33540             if((!c.titlebar && !c.title) || c.titlebar === false){
33541                 this.titleEl.hide();
33542             }else{
33543                 this.titleEl.show();
33544                 if(c.title){
33545                     this.titleTextEl.innerHTML = c.title;
33546                 }
33547             }
33548         //}
33549         this.duration = c.duration || .30;
33550         this.slideDuration = c.slideDuration || .45;
33551         this.config = c;
33552         if(c.collapsed){
33553             this.collapse(true);
33554         }
33555         if(c.hidden){
33556             this.hide();
33557         }
33558     },
33559     /**
33560      * Returns true if this region is currently visible.
33561      * @return {Boolean}
33562      */
33563     isVisible : function(){
33564         return this.visible;
33565     },
33566
33567     /**
33568      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33569      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33570      */
33571     setCollapsedTitle : function(title){
33572         title = title || "&#160;";
33573         if(this.collapsedTitleTextEl){
33574             this.collapsedTitleTextEl.innerHTML = title;
33575         }
33576     },
33577
33578     getBox : function(){
33579         var b;
33580         if(!this.collapsed){
33581             b = this.el.getBox(false, true);
33582         }else{
33583             b = this.collapsedEl.getBox(false, true);
33584         }
33585         return b;
33586     },
33587
33588     getMargins : function(){
33589         return this.collapsed ? this.cmargins : this.margins;
33590     },
33591
33592     highlight : function(){
33593         this.el.addClass("x-layout-panel-dragover");
33594     },
33595
33596     unhighlight : function(){
33597         this.el.removeClass("x-layout-panel-dragover");
33598     },
33599
33600     updateBox : function(box){
33601         this.box = box;
33602         if(!this.collapsed){
33603             this.el.dom.style.left = box.x + "px";
33604             this.el.dom.style.top = box.y + "px";
33605             this.updateBody(box.width, box.height);
33606         }else{
33607             this.collapsedEl.dom.style.left = box.x + "px";
33608             this.collapsedEl.dom.style.top = box.y + "px";
33609             this.collapsedEl.setSize(box.width, box.height);
33610         }
33611         if(this.tabs){
33612             this.tabs.autoSizeTabs();
33613         }
33614     },
33615
33616     updateBody : function(w, h){
33617         if(w !== null){
33618             this.el.setWidth(w);
33619             w -= this.el.getBorderWidth("rl");
33620             if(this.config.adjustments){
33621                 w += this.config.adjustments[0];
33622             }
33623         }
33624         if(h !== null){
33625             this.el.setHeight(h);
33626             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33627             h -= this.el.getBorderWidth("tb");
33628             if(this.config.adjustments){
33629                 h += this.config.adjustments[1];
33630             }
33631             this.bodyEl.setHeight(h);
33632             if(this.tabs){
33633                 h = this.tabs.syncHeight(h);
33634             }
33635         }
33636         if(this.panelSize){
33637             w = w !== null ? w : this.panelSize.width;
33638             h = h !== null ? h : this.panelSize.height;
33639         }
33640         if(this.activePanel){
33641             var el = this.activePanel.getEl();
33642             w = w !== null ? w : el.getWidth();
33643             h = h !== null ? h : el.getHeight();
33644             this.panelSize = {width: w, height: h};
33645             this.activePanel.setSize(w, h);
33646         }
33647         if(Roo.isIE && this.tabs){
33648             this.tabs.el.repaint();
33649         }
33650     },
33651
33652     /**
33653      * Returns the container element for this region.
33654      * @return {Roo.Element}
33655      */
33656     getEl : function(){
33657         return this.el;
33658     },
33659
33660     /**
33661      * Hides this region.
33662      */
33663     hide : function(){
33664         if(!this.collapsed){
33665             this.el.dom.style.left = "-2000px";
33666             this.el.hide();
33667         }else{
33668             this.collapsedEl.dom.style.left = "-2000px";
33669             this.collapsedEl.hide();
33670         }
33671         this.visible = false;
33672         this.fireEvent("visibilitychange", this, false);
33673     },
33674
33675     /**
33676      * Shows this region if it was previously hidden.
33677      */
33678     show : function(){
33679         if(!this.collapsed){
33680             this.el.show();
33681         }else{
33682             this.collapsedEl.show();
33683         }
33684         this.visible = true;
33685         this.fireEvent("visibilitychange", this, true);
33686     },
33687
33688     closeClicked : function(){
33689         if(this.activePanel){
33690             this.remove(this.activePanel);
33691         }
33692     },
33693
33694     collapseClick : function(e){
33695         if(this.isSlid){
33696            e.stopPropagation();
33697            this.slideIn();
33698         }else{
33699            e.stopPropagation();
33700            this.slideOut();
33701         }
33702     },
33703
33704     /**
33705      * Collapses this region.
33706      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33707      */
33708     collapse : function(skipAnim){
33709         if(this.collapsed) return;
33710         this.collapsed = true;
33711         if(this.split){
33712             this.split.el.hide();
33713         }
33714         if(this.config.animate && skipAnim !== true){
33715             this.fireEvent("invalidated", this);
33716             this.animateCollapse();
33717         }else{
33718             this.el.setLocation(-20000,-20000);
33719             this.el.hide();
33720             this.collapsedEl.show();
33721             this.fireEvent("collapsed", this);
33722             this.fireEvent("invalidated", this);
33723         }
33724     },
33725
33726     animateCollapse : function(){
33727         // overridden
33728     },
33729
33730     /**
33731      * Expands this region if it was previously collapsed.
33732      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33733      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33734      */
33735     expand : function(e, skipAnim){
33736         if(e) e.stopPropagation();
33737         if(!this.collapsed || this.el.hasActiveFx()) return;
33738         if(this.isSlid){
33739             this.afterSlideIn();
33740             skipAnim = true;
33741         }
33742         this.collapsed = false;
33743         if(this.config.animate && skipAnim !== true){
33744             this.animateExpand();
33745         }else{
33746             this.el.show();
33747             if(this.split){
33748                 this.split.el.show();
33749             }
33750             this.collapsedEl.setLocation(-2000,-2000);
33751             this.collapsedEl.hide();
33752             this.fireEvent("invalidated", this);
33753             this.fireEvent("expanded", this);
33754         }
33755     },
33756
33757     animateExpand : function(){
33758         // overridden
33759     },
33760
33761     initTabs : function()
33762     {
33763         this.bodyEl.setStyle("overflow", "hidden");
33764         var ts = new Roo.TabPanel(
33765                 this.bodyEl.dom,
33766                 {
33767                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33768                     disableTooltips: this.config.disableTabTips,
33769                     toolbar : this.config.toolbar
33770                 }
33771         );
33772         if(this.config.hideTabs){
33773             ts.stripWrap.setDisplayed(false);
33774         }
33775         this.tabs = ts;
33776         ts.resizeTabs = this.config.resizeTabs === true;
33777         ts.minTabWidth = this.config.minTabWidth || 40;
33778         ts.maxTabWidth = this.config.maxTabWidth || 250;
33779         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33780         ts.monitorResize = false;
33781         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33782         ts.bodyEl.addClass('x-layout-tabs-body');
33783         this.panels.each(this.initPanelAsTab, this);
33784     },
33785
33786     initPanelAsTab : function(panel){
33787         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33788                     this.config.closeOnTab && panel.isClosable());
33789         if(panel.tabTip !== undefined){
33790             ti.setTooltip(panel.tabTip);
33791         }
33792         ti.on("activate", function(){
33793               this.setActivePanel(panel);
33794         }, this);
33795         if(this.config.closeOnTab){
33796             ti.on("beforeclose", function(t, e){
33797                 e.cancel = true;
33798                 this.remove(panel);
33799             }, this);
33800         }
33801         return ti;
33802     },
33803
33804     updatePanelTitle : function(panel, title){
33805         if(this.activePanel == panel){
33806             this.updateTitle(title);
33807         }
33808         if(this.tabs){
33809             var ti = this.tabs.getTab(panel.getEl().id);
33810             ti.setText(title);
33811             if(panel.tabTip !== undefined){
33812                 ti.setTooltip(panel.tabTip);
33813             }
33814         }
33815     },
33816
33817     updateTitle : function(title){
33818         if(this.titleTextEl && !this.config.title){
33819             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33820         }
33821     },
33822
33823     setActivePanel : function(panel){
33824         panel = this.getPanel(panel);
33825         if(this.activePanel && this.activePanel != panel){
33826             this.activePanel.setActiveState(false);
33827         }
33828         this.activePanel = panel;
33829         panel.setActiveState(true);
33830         if(this.panelSize){
33831             panel.setSize(this.panelSize.width, this.panelSize.height);
33832         }
33833         if(this.closeBtn){
33834             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33835         }
33836         this.updateTitle(panel.getTitle());
33837         if(this.tabs){
33838             this.fireEvent("invalidated", this);
33839         }
33840         this.fireEvent("panelactivated", this, panel);
33841     },
33842
33843     /**
33844      * Shows the specified panel.
33845      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33846      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33847      */
33848     showPanel : function(panel){
33849         if(panel = this.getPanel(panel)){
33850             if(this.tabs){
33851                 var tab = this.tabs.getTab(panel.getEl().id);
33852                 if(tab.isHidden()){
33853                     this.tabs.unhideTab(tab.id);
33854                 }
33855                 tab.activate();
33856             }else{
33857                 this.setActivePanel(panel);
33858             }
33859         }
33860         return panel;
33861     },
33862
33863     /**
33864      * Get the active panel for this region.
33865      * @return {Roo.ContentPanel} The active panel or null
33866      */
33867     getActivePanel : function(){
33868         return this.activePanel;
33869     },
33870
33871     validateVisibility : function(){
33872         if(this.panels.getCount() < 1){
33873             this.updateTitle("&#160;");
33874             this.closeBtn.hide();
33875             this.hide();
33876         }else{
33877             if(!this.isVisible()){
33878                 this.show();
33879             }
33880         }
33881     },
33882
33883     /**
33884      * Adds the passed ContentPanel(s) to this region.
33885      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33886      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33887      */
33888     add : function(panel){
33889         if(arguments.length > 1){
33890             for(var i = 0, len = arguments.length; i < len; i++) {
33891                 this.add(arguments[i]);
33892             }
33893             return null;
33894         }
33895         if(this.hasPanel(panel)){
33896             this.showPanel(panel);
33897             return panel;
33898         }
33899         panel.setRegion(this);
33900         this.panels.add(panel);
33901         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33902             this.bodyEl.dom.appendChild(panel.getEl().dom);
33903             if(panel.background !== true){
33904                 this.setActivePanel(panel);
33905             }
33906             this.fireEvent("paneladded", this, panel);
33907             return panel;
33908         }
33909         if(!this.tabs){
33910             this.initTabs();
33911         }else{
33912             this.initPanelAsTab(panel);
33913         }
33914         if(panel.background !== true){
33915             this.tabs.activate(panel.getEl().id);
33916         }
33917         this.fireEvent("paneladded", this, panel);
33918         return panel;
33919     },
33920
33921     /**
33922      * Hides the tab for the specified panel.
33923      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33924      */
33925     hidePanel : function(panel){
33926         if(this.tabs && (panel = this.getPanel(panel))){
33927             this.tabs.hideTab(panel.getEl().id);
33928         }
33929     },
33930
33931     /**
33932      * Unhides the tab for a previously hidden panel.
33933      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33934      */
33935     unhidePanel : function(panel){
33936         if(this.tabs && (panel = this.getPanel(panel))){
33937             this.tabs.unhideTab(panel.getEl().id);
33938         }
33939     },
33940
33941     clearPanels : function(){
33942         while(this.panels.getCount() > 0){
33943              this.remove(this.panels.first());
33944         }
33945     },
33946
33947     /**
33948      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33949      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33950      * @param {Boolean} preservePanel Overrides the config preservePanel option
33951      * @return {Roo.ContentPanel} The panel that was removed
33952      */
33953     remove : function(panel, preservePanel){
33954         panel = this.getPanel(panel);
33955         if(!panel){
33956             return null;
33957         }
33958         var e = {};
33959         this.fireEvent("beforeremove", this, panel, e);
33960         if(e.cancel === true){
33961             return null;
33962         }
33963         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33964         var panelId = panel.getId();
33965         this.panels.removeKey(panelId);
33966         if(preservePanel){
33967             document.body.appendChild(panel.getEl().dom);
33968         }
33969         if(this.tabs){
33970             this.tabs.removeTab(panel.getEl().id);
33971         }else if (!preservePanel){
33972             this.bodyEl.dom.removeChild(panel.getEl().dom);
33973         }
33974         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33975             var p = this.panels.first();
33976             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33977             tempEl.appendChild(p.getEl().dom);
33978             this.bodyEl.update("");
33979             this.bodyEl.dom.appendChild(p.getEl().dom);
33980             tempEl = null;
33981             this.updateTitle(p.getTitle());
33982             this.tabs = null;
33983             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33984             this.setActivePanel(p);
33985         }
33986         panel.setRegion(null);
33987         if(this.activePanel == panel){
33988             this.activePanel = null;
33989         }
33990         if(this.config.autoDestroy !== false && preservePanel !== true){
33991             try{panel.destroy();}catch(e){}
33992         }
33993         this.fireEvent("panelremoved", this, panel);
33994         return panel;
33995     },
33996
33997     /**
33998      * Returns the TabPanel component used by this region
33999      * @return {Roo.TabPanel}
34000      */
34001     getTabs : function(){
34002         return this.tabs;
34003     },
34004
34005     createTool : function(parentEl, className){
34006         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34007             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34008         btn.addClassOnOver("x-layout-tools-button-over");
34009         return btn;
34010     }
34011 });/*
34012  * Based on:
34013  * Ext JS Library 1.1.1
34014  * Copyright(c) 2006-2007, Ext JS, LLC.
34015  *
34016  * Originally Released Under LGPL - original licence link has changed is not relivant.
34017  *
34018  * Fork - LGPL
34019  * <script type="text/javascript">
34020  */
34021  
34022
34023
34024 /**
34025  * @class Roo.SplitLayoutRegion
34026  * @extends Roo.LayoutRegion
34027  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34028  */
34029 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34030     this.cursor = cursor;
34031     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34032 };
34033
34034 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34035     splitTip : "Drag to resize.",
34036     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34037     useSplitTips : false,
34038
34039     applyConfig : function(config){
34040         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34041         if(config.split){
34042             if(!this.split){
34043                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34044                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34045                 /** The SplitBar for this region 
34046                 * @type Roo.SplitBar */
34047                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34048                 this.split.on("moved", this.onSplitMove, this);
34049                 this.split.useShim = config.useShim === true;
34050                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34051                 if(this.useSplitTips){
34052                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34053                 }
34054                 if(config.collapsible){
34055                     this.split.el.on("dblclick", this.collapse,  this);
34056                 }
34057             }
34058             if(typeof config.minSize != "undefined"){
34059                 this.split.minSize = config.minSize;
34060             }
34061             if(typeof config.maxSize != "undefined"){
34062                 this.split.maxSize = config.maxSize;
34063             }
34064             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34065                 this.hideSplitter();
34066             }
34067         }
34068     },
34069
34070     getHMaxSize : function(){
34071          var cmax = this.config.maxSize || 10000;
34072          var center = this.mgr.getRegion("center");
34073          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34074     },
34075
34076     getVMaxSize : function(){
34077          var cmax = this.config.maxSize || 10000;
34078          var center = this.mgr.getRegion("center");
34079          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34080     },
34081
34082     onSplitMove : function(split, newSize){
34083         this.fireEvent("resized", this, newSize);
34084     },
34085     
34086     /** 
34087      * Returns the {@link Roo.SplitBar} for this region.
34088      * @return {Roo.SplitBar}
34089      */
34090     getSplitBar : function(){
34091         return this.split;
34092     },
34093     
34094     hide : function(){
34095         this.hideSplitter();
34096         Roo.SplitLayoutRegion.superclass.hide.call(this);
34097     },
34098
34099     hideSplitter : function(){
34100         if(this.split){
34101             this.split.el.setLocation(-2000,-2000);
34102             this.split.el.hide();
34103         }
34104     },
34105
34106     show : function(){
34107         if(this.split){
34108             this.split.el.show();
34109         }
34110         Roo.SplitLayoutRegion.superclass.show.call(this);
34111     },
34112     
34113     beforeSlide: function(){
34114         if(Roo.isGecko){// firefox overflow auto bug workaround
34115             this.bodyEl.clip();
34116             if(this.tabs) this.tabs.bodyEl.clip();
34117             if(this.activePanel){
34118                 this.activePanel.getEl().clip();
34119                 
34120                 if(this.activePanel.beforeSlide){
34121                     this.activePanel.beforeSlide();
34122                 }
34123             }
34124         }
34125     },
34126     
34127     afterSlide : function(){
34128         if(Roo.isGecko){// firefox overflow auto bug workaround
34129             this.bodyEl.unclip();
34130             if(this.tabs) this.tabs.bodyEl.unclip();
34131             if(this.activePanel){
34132                 this.activePanel.getEl().unclip();
34133                 if(this.activePanel.afterSlide){
34134                     this.activePanel.afterSlide();
34135                 }
34136             }
34137         }
34138     },
34139
34140     initAutoHide : function(){
34141         if(this.autoHide !== false){
34142             if(!this.autoHideHd){
34143                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34144                 this.autoHideHd = {
34145                     "mouseout": function(e){
34146                         if(!e.within(this.el, true)){
34147                             st.delay(500);
34148                         }
34149                     },
34150                     "mouseover" : function(e){
34151                         st.cancel();
34152                     },
34153                     scope : this
34154                 };
34155             }
34156             this.el.on(this.autoHideHd);
34157         }
34158     },
34159
34160     clearAutoHide : function(){
34161         if(this.autoHide !== false){
34162             this.el.un("mouseout", this.autoHideHd.mouseout);
34163             this.el.un("mouseover", this.autoHideHd.mouseover);
34164         }
34165     },
34166
34167     clearMonitor : function(){
34168         Roo.get(document).un("click", this.slideInIf, this);
34169     },
34170
34171     // these names are backwards but not changed for compat
34172     slideOut : function(){
34173         if(this.isSlid || this.el.hasActiveFx()){
34174             return;
34175         }
34176         this.isSlid = true;
34177         if(this.collapseBtn){
34178             this.collapseBtn.hide();
34179         }
34180         this.closeBtnState = this.closeBtn.getStyle('display');
34181         this.closeBtn.hide();
34182         if(this.stickBtn){
34183             this.stickBtn.show();
34184         }
34185         this.el.show();
34186         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34187         this.beforeSlide();
34188         this.el.setStyle("z-index", 10001);
34189         this.el.slideIn(this.getSlideAnchor(), {
34190             callback: function(){
34191                 this.afterSlide();
34192                 this.initAutoHide();
34193                 Roo.get(document).on("click", this.slideInIf, this);
34194                 this.fireEvent("slideshow", this);
34195             },
34196             scope: this,
34197             block: true
34198         });
34199     },
34200
34201     afterSlideIn : function(){
34202         this.clearAutoHide();
34203         this.isSlid = false;
34204         this.clearMonitor();
34205         this.el.setStyle("z-index", "");
34206         if(this.collapseBtn){
34207             this.collapseBtn.show();
34208         }
34209         this.closeBtn.setStyle('display', this.closeBtnState);
34210         if(this.stickBtn){
34211             this.stickBtn.hide();
34212         }
34213         this.fireEvent("slidehide", this);
34214     },
34215
34216     slideIn : function(cb){
34217         if(!this.isSlid || this.el.hasActiveFx()){
34218             Roo.callback(cb);
34219             return;
34220         }
34221         this.isSlid = false;
34222         this.beforeSlide();
34223         this.el.slideOut(this.getSlideAnchor(), {
34224             callback: function(){
34225                 this.el.setLeftTop(-10000, -10000);
34226                 this.afterSlide();
34227                 this.afterSlideIn();
34228                 Roo.callback(cb);
34229             },
34230             scope: this,
34231             block: true
34232         });
34233     },
34234     
34235     slideInIf : function(e){
34236         if(!e.within(this.el)){
34237             this.slideIn();
34238         }
34239     },
34240
34241     animateCollapse : function(){
34242         this.beforeSlide();
34243         this.el.setStyle("z-index", 20000);
34244         var anchor = this.getSlideAnchor();
34245         this.el.slideOut(anchor, {
34246             callback : function(){
34247                 this.el.setStyle("z-index", "");
34248                 this.collapsedEl.slideIn(anchor, {duration:.3});
34249                 this.afterSlide();
34250                 this.el.setLocation(-10000,-10000);
34251                 this.el.hide();
34252                 this.fireEvent("collapsed", this);
34253             },
34254             scope: this,
34255             block: true
34256         });
34257     },
34258
34259     animateExpand : function(){
34260         this.beforeSlide();
34261         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34262         this.el.setStyle("z-index", 20000);
34263         this.collapsedEl.hide({
34264             duration:.1
34265         });
34266         this.el.slideIn(this.getSlideAnchor(), {
34267             callback : function(){
34268                 this.el.setStyle("z-index", "");
34269                 this.afterSlide();
34270                 if(this.split){
34271                     this.split.el.show();
34272                 }
34273                 this.fireEvent("invalidated", this);
34274                 this.fireEvent("expanded", this);
34275             },
34276             scope: this,
34277             block: true
34278         });
34279     },
34280
34281     anchors : {
34282         "west" : "left",
34283         "east" : "right",
34284         "north" : "top",
34285         "south" : "bottom"
34286     },
34287
34288     sanchors : {
34289         "west" : "l",
34290         "east" : "r",
34291         "north" : "t",
34292         "south" : "b"
34293     },
34294
34295     canchors : {
34296         "west" : "tl-tr",
34297         "east" : "tr-tl",
34298         "north" : "tl-bl",
34299         "south" : "bl-tl"
34300     },
34301
34302     getAnchor : function(){
34303         return this.anchors[this.position];
34304     },
34305
34306     getCollapseAnchor : function(){
34307         return this.canchors[this.position];
34308     },
34309
34310     getSlideAnchor : function(){
34311         return this.sanchors[this.position];
34312     },
34313
34314     getAlignAdj : function(){
34315         var cm = this.cmargins;
34316         switch(this.position){
34317             case "west":
34318                 return [0, 0];
34319             break;
34320             case "east":
34321                 return [0, 0];
34322             break;
34323             case "north":
34324                 return [0, 0];
34325             break;
34326             case "south":
34327                 return [0, 0];
34328             break;
34329         }
34330     },
34331
34332     getExpandAdj : function(){
34333         var c = this.collapsedEl, cm = this.cmargins;
34334         switch(this.position){
34335             case "west":
34336                 return [-(cm.right+c.getWidth()+cm.left), 0];
34337             break;
34338             case "east":
34339                 return [cm.right+c.getWidth()+cm.left, 0];
34340             break;
34341             case "north":
34342                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34343             break;
34344             case "south":
34345                 return [0, cm.top+cm.bottom+c.getHeight()];
34346             break;
34347         }
34348     }
34349 });/*
34350  * Based on:
34351  * Ext JS Library 1.1.1
34352  * Copyright(c) 2006-2007, Ext JS, LLC.
34353  *
34354  * Originally Released Under LGPL - original licence link has changed is not relivant.
34355  *
34356  * Fork - LGPL
34357  * <script type="text/javascript">
34358  */
34359 /*
34360  * These classes are private internal classes
34361  */
34362 Roo.CenterLayoutRegion = function(mgr, config){
34363     Roo.LayoutRegion.call(this, mgr, config, "center");
34364     this.visible = true;
34365     this.minWidth = config.minWidth || 20;
34366     this.minHeight = config.minHeight || 20;
34367 };
34368
34369 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34370     hide : function(){
34371         // center panel can't be hidden
34372     },
34373     
34374     show : function(){
34375         // center panel can't be hidden
34376     },
34377     
34378     getMinWidth: function(){
34379         return this.minWidth;
34380     },
34381     
34382     getMinHeight: function(){
34383         return this.minHeight;
34384     }
34385 });
34386
34387
34388 Roo.NorthLayoutRegion = function(mgr, config){
34389     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34390     if(this.split){
34391         this.split.placement = Roo.SplitBar.TOP;
34392         this.split.orientation = Roo.SplitBar.VERTICAL;
34393         this.split.el.addClass("x-layout-split-v");
34394     }
34395     var size = config.initialSize || config.height;
34396     if(typeof size != "undefined"){
34397         this.el.setHeight(size);
34398     }
34399 };
34400 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34401     orientation: Roo.SplitBar.VERTICAL,
34402     getBox : function(){
34403         if(this.collapsed){
34404             return this.collapsedEl.getBox();
34405         }
34406         var box = this.el.getBox();
34407         if(this.split){
34408             box.height += this.split.el.getHeight();
34409         }
34410         return box;
34411     },
34412     
34413     updateBox : function(box){
34414         if(this.split && !this.collapsed){
34415             box.height -= this.split.el.getHeight();
34416             this.split.el.setLeft(box.x);
34417             this.split.el.setTop(box.y+box.height);
34418             this.split.el.setWidth(box.width);
34419         }
34420         if(this.collapsed){
34421             this.updateBody(box.width, null);
34422         }
34423         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34424     }
34425 });
34426
34427 Roo.SouthLayoutRegion = function(mgr, config){
34428     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34429     if(this.split){
34430         this.split.placement = Roo.SplitBar.BOTTOM;
34431         this.split.orientation = Roo.SplitBar.VERTICAL;
34432         this.split.el.addClass("x-layout-split-v");
34433     }
34434     var size = config.initialSize || config.height;
34435     if(typeof size != "undefined"){
34436         this.el.setHeight(size);
34437     }
34438 };
34439 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34440     orientation: Roo.SplitBar.VERTICAL,
34441     getBox : function(){
34442         if(this.collapsed){
34443             return this.collapsedEl.getBox();
34444         }
34445         var box = this.el.getBox();
34446         if(this.split){
34447             var sh = this.split.el.getHeight();
34448             box.height += sh;
34449             box.y -= sh;
34450         }
34451         return box;
34452     },
34453     
34454     updateBox : function(box){
34455         if(this.split && !this.collapsed){
34456             var sh = this.split.el.getHeight();
34457             box.height -= sh;
34458             box.y += sh;
34459             this.split.el.setLeft(box.x);
34460             this.split.el.setTop(box.y-sh);
34461             this.split.el.setWidth(box.width);
34462         }
34463         if(this.collapsed){
34464             this.updateBody(box.width, null);
34465         }
34466         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34467     }
34468 });
34469
34470 Roo.EastLayoutRegion = function(mgr, config){
34471     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34472     if(this.split){
34473         this.split.placement = Roo.SplitBar.RIGHT;
34474         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34475         this.split.el.addClass("x-layout-split-h");
34476     }
34477     var size = config.initialSize || config.width;
34478     if(typeof size != "undefined"){
34479         this.el.setWidth(size);
34480     }
34481 };
34482 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34483     orientation: Roo.SplitBar.HORIZONTAL,
34484     getBox : function(){
34485         if(this.collapsed){
34486             return this.collapsedEl.getBox();
34487         }
34488         var box = this.el.getBox();
34489         if(this.split){
34490             var sw = this.split.el.getWidth();
34491             box.width += sw;
34492             box.x -= sw;
34493         }
34494         return box;
34495     },
34496
34497     updateBox : function(box){
34498         if(this.split && !this.collapsed){
34499             var sw = this.split.el.getWidth();
34500             box.width -= sw;
34501             this.split.el.setLeft(box.x);
34502             this.split.el.setTop(box.y);
34503             this.split.el.setHeight(box.height);
34504             box.x += sw;
34505         }
34506         if(this.collapsed){
34507             this.updateBody(null, box.height);
34508         }
34509         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34510     }
34511 });
34512
34513 Roo.WestLayoutRegion = function(mgr, config){
34514     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34515     if(this.split){
34516         this.split.placement = Roo.SplitBar.LEFT;
34517         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34518         this.split.el.addClass("x-layout-split-h");
34519     }
34520     var size = config.initialSize || config.width;
34521     if(typeof size != "undefined"){
34522         this.el.setWidth(size);
34523     }
34524 };
34525 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34526     orientation: Roo.SplitBar.HORIZONTAL,
34527     getBox : function(){
34528         if(this.collapsed){
34529             return this.collapsedEl.getBox();
34530         }
34531         var box = this.el.getBox();
34532         if(this.split){
34533             box.width += this.split.el.getWidth();
34534         }
34535         return box;
34536     },
34537     
34538     updateBox : function(box){
34539         if(this.split && !this.collapsed){
34540             var sw = this.split.el.getWidth();
34541             box.width -= sw;
34542             this.split.el.setLeft(box.x+box.width);
34543             this.split.el.setTop(box.y);
34544             this.split.el.setHeight(box.height);
34545         }
34546         if(this.collapsed){
34547             this.updateBody(null, box.height);
34548         }
34549         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34550     }
34551 });
34552 /*
34553  * Based on:
34554  * Ext JS Library 1.1.1
34555  * Copyright(c) 2006-2007, Ext JS, LLC.
34556  *
34557  * Originally Released Under LGPL - original licence link has changed is not relivant.
34558  *
34559  * Fork - LGPL
34560  * <script type="text/javascript">
34561  */
34562  
34563  
34564 /*
34565  * Private internal class for reading and applying state
34566  */
34567 Roo.LayoutStateManager = function(layout){
34568      // default empty state
34569      this.state = {
34570         north: {},
34571         south: {},
34572         east: {},
34573         west: {}       
34574     };
34575 };
34576
34577 Roo.LayoutStateManager.prototype = {
34578     init : function(layout, provider){
34579         this.provider = provider;
34580         var state = provider.get(layout.id+"-layout-state");
34581         if(state){
34582             var wasUpdating = layout.isUpdating();
34583             if(!wasUpdating){
34584                 layout.beginUpdate();
34585             }
34586             for(var key in state){
34587                 if(typeof state[key] != "function"){
34588                     var rstate = state[key];
34589                     var r = layout.getRegion(key);
34590                     if(r && rstate){
34591                         if(rstate.size){
34592                             r.resizeTo(rstate.size);
34593                         }
34594                         if(rstate.collapsed == true){
34595                             r.collapse(true);
34596                         }else{
34597                             r.expand(null, true);
34598                         }
34599                     }
34600                 }
34601             }
34602             if(!wasUpdating){
34603                 layout.endUpdate();
34604             }
34605             this.state = state; 
34606         }
34607         this.layout = layout;
34608         layout.on("regionresized", this.onRegionResized, this);
34609         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34610         layout.on("regionexpanded", this.onRegionExpanded, this);
34611     },
34612     
34613     storeState : function(){
34614         this.provider.set(this.layout.id+"-layout-state", this.state);
34615     },
34616     
34617     onRegionResized : function(region, newSize){
34618         this.state[region.getPosition()].size = newSize;
34619         this.storeState();
34620     },
34621     
34622     onRegionCollapsed : function(region){
34623         this.state[region.getPosition()].collapsed = true;
34624         this.storeState();
34625     },
34626     
34627     onRegionExpanded : function(region){
34628         this.state[region.getPosition()].collapsed = false;
34629         this.storeState();
34630     }
34631 };/*
34632  * Based on:
34633  * Ext JS Library 1.1.1
34634  * Copyright(c) 2006-2007, Ext JS, LLC.
34635  *
34636  * Originally Released Under LGPL - original licence link has changed is not relivant.
34637  *
34638  * Fork - LGPL
34639  * <script type="text/javascript">
34640  */
34641 /**
34642  * @class Roo.ContentPanel
34643  * @extends Roo.util.Observable
34644  * A basic ContentPanel element.
34645  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34646  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34647  * @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
34648  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34649  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34650  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34651  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34652  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34653  * @cfg {String} title          The title for this panel
34654  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34655  * @cfg {String} url            Calls {@link #setUrl} with this value
34656  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34657  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34658  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34659  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34660
34661  * @constructor
34662  * Create a new ContentPanel.
34663  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34664  * @param {String/Object} config A string to set only the title or a config object
34665  * @param {String} content (optional) Set the HTML content for this panel
34666  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34667  */
34668 Roo.ContentPanel = function(el, config, content){
34669     
34670      
34671     /*
34672     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34673         config = el;
34674         el = Roo.id();
34675     }
34676     if (config && config.parentLayout) { 
34677         el = config.parentLayout.el.createChild(); 
34678     }
34679     */
34680     if(el.autoCreate){ // xtype is available if this is called from factory
34681         config = el;
34682         el = Roo.id();
34683     }
34684     this.el = Roo.get(el);
34685     if(!this.el && config && config.autoCreate){
34686         if(typeof config.autoCreate == "object"){
34687             if(!config.autoCreate.id){
34688                 config.autoCreate.id = config.id||el;
34689             }
34690             this.el = Roo.DomHelper.append(document.body,
34691                         config.autoCreate, true);
34692         }else{
34693             this.el = Roo.DomHelper.append(document.body,
34694                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34695         }
34696     }
34697     this.closable = false;
34698     this.loaded = false;
34699     this.active = false;
34700     if(typeof config == "string"){
34701         this.title = config;
34702     }else{
34703         Roo.apply(this, config);
34704     }
34705     
34706     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34707         this.wrapEl = this.el.wrap();
34708         this.toolbar.container = this.el.insertSibling(false, 'before');
34709         this.toolbar = new Roo.Toolbar(this.toolbar);
34710     }
34711     
34712     // xtype created footer. - not sure if will work as we normally have to render first..
34713     if (this.footer && !this.footer.el && this.footer.xtype) {
34714         if (!this.wrapEl) {
34715             this.wrapEl = this.el.wrap();
34716         }
34717     
34718         this.footer.container = this.wrapEl.createChild();
34719          
34720         this.footer = Roo.factory(this.footer, Roo);
34721         
34722     }
34723     
34724     if(this.resizeEl){
34725         this.resizeEl = Roo.get(this.resizeEl, true);
34726     }else{
34727         this.resizeEl = this.el;
34728     }
34729     // handle view.xtype
34730     
34731  
34732     
34733     
34734     this.addEvents({
34735         /**
34736          * @event activate
34737          * Fires when this panel is activated. 
34738          * @param {Roo.ContentPanel} this
34739          */
34740         "activate" : true,
34741         /**
34742          * @event deactivate
34743          * Fires when this panel is activated. 
34744          * @param {Roo.ContentPanel} this
34745          */
34746         "deactivate" : true,
34747
34748         /**
34749          * @event resize
34750          * Fires when this panel is resized if fitToFrame is true.
34751          * @param {Roo.ContentPanel} this
34752          * @param {Number} width The width after any component adjustments
34753          * @param {Number} height The height after any component adjustments
34754          */
34755         "resize" : true,
34756         
34757          /**
34758          * @event render
34759          * Fires when this tab is created
34760          * @param {Roo.ContentPanel} this
34761          */
34762         "render" : true
34763         
34764         
34765         
34766     });
34767     
34768
34769     
34770     
34771     if(this.autoScroll){
34772         this.resizeEl.setStyle("overflow", "auto");
34773     } else {
34774         // fix randome scrolling
34775         this.el.on('scroll', function() {
34776             Roo.log('fix random scolling');
34777             this.scrollTo('top',0); 
34778         });
34779     }
34780     content = content || this.content;
34781     if(content){
34782         this.setContent(content);
34783     }
34784     if(config && config.url){
34785         this.setUrl(this.url, this.params, this.loadOnce);
34786     }
34787     
34788     
34789     
34790     Roo.ContentPanel.superclass.constructor.call(this);
34791     
34792     if (this.view && typeof(this.view.xtype) != 'undefined') {
34793         this.view.el = this.el.appendChild(document.createElement("div"));
34794         this.view = Roo.factory(this.view); 
34795         this.view.render  &&  this.view.render(false, '');  
34796     }
34797     
34798     
34799     this.fireEvent('render', this);
34800 };
34801
34802 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34803     tabTip:'',
34804     setRegion : function(region){
34805         this.region = region;
34806         if(region){
34807            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34808         }else{
34809            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34810         } 
34811     },
34812     
34813     /**
34814      * Returns the toolbar for this Panel if one was configured. 
34815      * @return {Roo.Toolbar} 
34816      */
34817     getToolbar : function(){
34818         return this.toolbar;
34819     },
34820     
34821     setActiveState : function(active){
34822         this.active = active;
34823         if(!active){
34824             this.fireEvent("deactivate", this);
34825         }else{
34826             this.fireEvent("activate", this);
34827         }
34828     },
34829     /**
34830      * Updates this panel's element
34831      * @param {String} content The new content
34832      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34833     */
34834     setContent : function(content, loadScripts){
34835         this.el.update(content, loadScripts);
34836     },
34837
34838     ignoreResize : function(w, h){
34839         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34840             return true;
34841         }else{
34842             this.lastSize = {width: w, height: h};
34843             return false;
34844         }
34845     },
34846     /**
34847      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34848      * @return {Roo.UpdateManager} The UpdateManager
34849      */
34850     getUpdateManager : function(){
34851         return this.el.getUpdateManager();
34852     },
34853      /**
34854      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34855      * @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:
34856 <pre><code>
34857 panel.load({
34858     url: "your-url.php",
34859     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34860     callback: yourFunction,
34861     scope: yourObject, //(optional scope)
34862     discardUrl: false,
34863     nocache: false,
34864     text: "Loading...",
34865     timeout: 30,
34866     scripts: false
34867 });
34868 </code></pre>
34869      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34870      * 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.
34871      * @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}
34872      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34873      * @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.
34874      * @return {Roo.ContentPanel} this
34875      */
34876     load : function(){
34877         var um = this.el.getUpdateManager();
34878         um.update.apply(um, arguments);
34879         return this;
34880     },
34881
34882
34883     /**
34884      * 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.
34885      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34886      * @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)
34887      * @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)
34888      * @return {Roo.UpdateManager} The UpdateManager
34889      */
34890     setUrl : function(url, params, loadOnce){
34891         if(this.refreshDelegate){
34892             this.removeListener("activate", this.refreshDelegate);
34893         }
34894         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34895         this.on("activate", this.refreshDelegate);
34896         return this.el.getUpdateManager();
34897     },
34898     
34899     _handleRefresh : function(url, params, loadOnce){
34900         if(!loadOnce || !this.loaded){
34901             var updater = this.el.getUpdateManager();
34902             updater.update(url, params, this._setLoaded.createDelegate(this));
34903         }
34904     },
34905     
34906     _setLoaded : function(){
34907         this.loaded = true;
34908     }, 
34909     
34910     /**
34911      * Returns this panel's id
34912      * @return {String} 
34913      */
34914     getId : function(){
34915         return this.el.id;
34916     },
34917     
34918     /** 
34919      * Returns this panel's element - used by regiosn to add.
34920      * @return {Roo.Element} 
34921      */
34922     getEl : function(){
34923         return this.wrapEl || this.el;
34924     },
34925     
34926     adjustForComponents : function(width, height)
34927     {
34928         //Roo.log('adjustForComponents ');
34929         if(this.resizeEl != this.el){
34930             width -= this.el.getFrameWidth('lr');
34931             height -= this.el.getFrameWidth('tb');
34932         }
34933         if(this.toolbar){
34934             var te = this.toolbar.getEl();
34935             height -= te.getHeight();
34936             te.setWidth(width);
34937         }
34938         if(this.footer){
34939             var te = this.footer.getEl();
34940             Roo.log("footer:" + te.getHeight());
34941             
34942             height -= te.getHeight();
34943             te.setWidth(width);
34944         }
34945         
34946         
34947         if(this.adjustments){
34948             width += this.adjustments[0];
34949             height += this.adjustments[1];
34950         }
34951         return {"width": width, "height": height};
34952     },
34953     
34954     setSize : function(width, height){
34955         if(this.fitToFrame && !this.ignoreResize(width, height)){
34956             if(this.fitContainer && this.resizeEl != this.el){
34957                 this.el.setSize(width, height);
34958             }
34959             var size = this.adjustForComponents(width, height);
34960             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34961             this.fireEvent('resize', this, size.width, size.height);
34962         }
34963     },
34964     
34965     /**
34966      * Returns this panel's title
34967      * @return {String} 
34968      */
34969     getTitle : function(){
34970         return this.title;
34971     },
34972     
34973     /**
34974      * Set this panel's title
34975      * @param {String} title
34976      */
34977     setTitle : function(title){
34978         this.title = title;
34979         if(this.region){
34980             this.region.updatePanelTitle(this, title);
34981         }
34982     },
34983     
34984     /**
34985      * Returns true is this panel was configured to be closable
34986      * @return {Boolean} 
34987      */
34988     isClosable : function(){
34989         return this.closable;
34990     },
34991     
34992     beforeSlide : function(){
34993         this.el.clip();
34994         this.resizeEl.clip();
34995     },
34996     
34997     afterSlide : function(){
34998         this.el.unclip();
34999         this.resizeEl.unclip();
35000     },
35001     
35002     /**
35003      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35004      *   Will fail silently if the {@link #setUrl} method has not been called.
35005      *   This does not activate the panel, just updates its content.
35006      */
35007     refresh : function(){
35008         if(this.refreshDelegate){
35009            this.loaded = false;
35010            this.refreshDelegate();
35011         }
35012     },
35013     
35014     /**
35015      * Destroys this panel
35016      */
35017     destroy : function(){
35018         this.el.removeAllListeners();
35019         var tempEl = document.createElement("span");
35020         tempEl.appendChild(this.el.dom);
35021         tempEl.innerHTML = "";
35022         this.el.remove();
35023         this.el = null;
35024     },
35025     
35026     /**
35027      * form - if the content panel contains a form - this is a reference to it.
35028      * @type {Roo.form.Form}
35029      */
35030     form : false,
35031     /**
35032      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35033      *    This contains a reference to it.
35034      * @type {Roo.View}
35035      */
35036     view : false,
35037     
35038       /**
35039      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35040      * <pre><code>
35041
35042 layout.addxtype({
35043        xtype : 'Form',
35044        items: [ .... ]
35045    }
35046 );
35047
35048 </code></pre>
35049      * @param {Object} cfg Xtype definition of item to add.
35050      */
35051     
35052     addxtype : function(cfg) {
35053         // add form..
35054         if (cfg.xtype.match(/^Form$/)) {
35055             
35056             var el;
35057             //if (this.footer) {
35058             //    el = this.footer.container.insertSibling(false, 'before');
35059             //} else {
35060                 el = this.el.createChild();
35061             //}
35062
35063             this.form = new  Roo.form.Form(cfg);
35064             
35065             
35066             if ( this.form.allItems.length) this.form.render(el.dom);
35067             return this.form;
35068         }
35069         // should only have one of theses..
35070         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35071             // views.. should not be just added - used named prop 'view''
35072             
35073             cfg.el = this.el.appendChild(document.createElement("div"));
35074             // factory?
35075             
35076             var ret = new Roo.factory(cfg);
35077              
35078              ret.render && ret.render(false, ''); // render blank..
35079             this.view = ret;
35080             return ret;
35081         }
35082         return false;
35083     }
35084 });
35085
35086 /**
35087  * @class Roo.GridPanel
35088  * @extends Roo.ContentPanel
35089  * @constructor
35090  * Create a new GridPanel.
35091  * @param {Roo.grid.Grid} grid The grid for this panel
35092  * @param {String/Object} config A string to set only the panel's title, or a config object
35093  */
35094 Roo.GridPanel = function(grid, config){
35095     
35096   
35097     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35098         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35099         
35100     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35101     
35102     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35103     
35104     if(this.toolbar){
35105         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35106     }
35107     // xtype created footer. - not sure if will work as we normally have to render first..
35108     if (this.footer && !this.footer.el && this.footer.xtype) {
35109         
35110         this.footer.container = this.grid.getView().getFooterPanel(true);
35111         this.footer.dataSource = this.grid.dataSource;
35112         this.footer = Roo.factory(this.footer, Roo);
35113         
35114     }
35115     
35116     grid.monitorWindowResize = false; // turn off autosizing
35117     grid.autoHeight = false;
35118     grid.autoWidth = false;
35119     this.grid = grid;
35120     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35121 };
35122
35123 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35124     getId : function(){
35125         return this.grid.id;
35126     },
35127     
35128     /**
35129      * Returns the grid for this panel
35130      * @return {Roo.grid.Grid} 
35131      */
35132     getGrid : function(){
35133         return this.grid;    
35134     },
35135     
35136     setSize : function(width, height){
35137         if(!this.ignoreResize(width, height)){
35138             var grid = this.grid;
35139             var size = this.adjustForComponents(width, height);
35140             grid.getGridEl().setSize(size.width, size.height);
35141             grid.autoSize();
35142         }
35143     },
35144     
35145     beforeSlide : function(){
35146         this.grid.getView().scroller.clip();
35147     },
35148     
35149     afterSlide : function(){
35150         this.grid.getView().scroller.unclip();
35151     },
35152     
35153     destroy : function(){
35154         this.grid.destroy();
35155         delete this.grid;
35156         Roo.GridPanel.superclass.destroy.call(this); 
35157     }
35158 });
35159
35160
35161 /**
35162  * @class Roo.NestedLayoutPanel
35163  * @extends Roo.ContentPanel
35164  * @constructor
35165  * Create a new NestedLayoutPanel.
35166  * 
35167  * 
35168  * @param {Roo.BorderLayout} layout The layout for this panel
35169  * @param {String/Object} config A string to set only the title or a config object
35170  */
35171 Roo.NestedLayoutPanel = function(layout, config)
35172 {
35173     // construct with only one argument..
35174     /* FIXME - implement nicer consturctors
35175     if (layout.layout) {
35176         config = layout;
35177         layout = config.layout;
35178         delete config.layout;
35179     }
35180     if (layout.xtype && !layout.getEl) {
35181         // then layout needs constructing..
35182         layout = Roo.factory(layout, Roo);
35183     }
35184     */
35185     
35186     
35187     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35188     
35189     layout.monitorWindowResize = false; // turn off autosizing
35190     this.layout = layout;
35191     this.layout.getEl().addClass("x-layout-nested-layout");
35192     
35193     
35194     
35195     
35196 };
35197
35198 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35199
35200     setSize : function(width, height){
35201         if(!this.ignoreResize(width, height)){
35202             var size = this.adjustForComponents(width, height);
35203             var el = this.layout.getEl();
35204             el.setSize(size.width, size.height);
35205             var touch = el.dom.offsetWidth;
35206             this.layout.layout();
35207             // ie requires a double layout on the first pass
35208             if(Roo.isIE && !this.initialized){
35209                 this.initialized = true;
35210                 this.layout.layout();
35211             }
35212         }
35213     },
35214     
35215     // activate all subpanels if not currently active..
35216     
35217     setActiveState : function(active){
35218         this.active = active;
35219         if(!active){
35220             this.fireEvent("deactivate", this);
35221             return;
35222         }
35223         
35224         this.fireEvent("activate", this);
35225         // not sure if this should happen before or after..
35226         if (!this.layout) {
35227             return; // should not happen..
35228         }
35229         var reg = false;
35230         for (var r in this.layout.regions) {
35231             reg = this.layout.getRegion(r);
35232             if (reg.getActivePanel()) {
35233                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35234                 reg.setActivePanel(reg.getActivePanel());
35235                 continue;
35236             }
35237             if (!reg.panels.length) {
35238                 continue;
35239             }
35240             reg.showPanel(reg.getPanel(0));
35241         }
35242         
35243         
35244         
35245         
35246     },
35247     
35248     /**
35249      * Returns the nested BorderLayout for this panel
35250      * @return {Roo.BorderLayout} 
35251      */
35252     getLayout : function(){
35253         return this.layout;
35254     },
35255     
35256      /**
35257      * Adds a xtype elements to the layout of the nested panel
35258      * <pre><code>
35259
35260 panel.addxtype({
35261        xtype : 'ContentPanel',
35262        region: 'west',
35263        items: [ .... ]
35264    }
35265 );
35266
35267 panel.addxtype({
35268         xtype : 'NestedLayoutPanel',
35269         region: 'west',
35270         layout: {
35271            center: { },
35272            west: { }   
35273         },
35274         items : [ ... list of content panels or nested layout panels.. ]
35275    }
35276 );
35277 </code></pre>
35278      * @param {Object} cfg Xtype definition of item to add.
35279      */
35280     addxtype : function(cfg) {
35281         return this.layout.addxtype(cfg);
35282     
35283     }
35284 });
35285
35286 Roo.ScrollPanel = function(el, config, content){
35287     config = config || {};
35288     config.fitToFrame = true;
35289     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35290     
35291     this.el.dom.style.overflow = "hidden";
35292     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35293     this.el.removeClass("x-layout-inactive-content");
35294     this.el.on("mousewheel", this.onWheel, this);
35295
35296     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35297     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35298     up.unselectable(); down.unselectable();
35299     up.on("click", this.scrollUp, this);
35300     down.on("click", this.scrollDown, this);
35301     up.addClassOnOver("x-scroller-btn-over");
35302     down.addClassOnOver("x-scroller-btn-over");
35303     up.addClassOnClick("x-scroller-btn-click");
35304     down.addClassOnClick("x-scroller-btn-click");
35305     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35306
35307     this.resizeEl = this.el;
35308     this.el = wrap; this.up = up; this.down = down;
35309 };
35310
35311 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35312     increment : 100,
35313     wheelIncrement : 5,
35314     scrollUp : function(){
35315         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35316     },
35317
35318     scrollDown : function(){
35319         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35320     },
35321
35322     afterScroll : function(){
35323         var el = this.resizeEl;
35324         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35325         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35326         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35327     },
35328
35329     setSize : function(){
35330         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35331         this.afterScroll();
35332     },
35333
35334     onWheel : function(e){
35335         var d = e.getWheelDelta();
35336         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35337         this.afterScroll();
35338         e.stopEvent();
35339     },
35340
35341     setContent : function(content, loadScripts){
35342         this.resizeEl.update(content, loadScripts);
35343     }
35344
35345 });
35346
35347
35348
35349
35350
35351
35352
35353
35354
35355 /**
35356  * @class Roo.TreePanel
35357  * @extends Roo.ContentPanel
35358  * @constructor
35359  * Create a new TreePanel. - defaults to fit/scoll contents.
35360  * @param {String/Object} config A string to set only the panel's title, or a config object
35361  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35362  */
35363 Roo.TreePanel = function(config){
35364     var el = config.el;
35365     var tree = config.tree;
35366     delete config.tree; 
35367     delete config.el; // hopefull!
35368     
35369     // wrapper for IE7 strict & safari scroll issue
35370     
35371     var treeEl = el.createChild();
35372     config.resizeEl = treeEl;
35373     
35374     
35375     
35376     Roo.TreePanel.superclass.constructor.call(this, el, config);
35377  
35378  
35379     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35380     //console.log(tree);
35381     this.on('activate', function()
35382     {
35383         if (this.tree.rendered) {
35384             return;
35385         }
35386         //console.log('render tree');
35387         this.tree.render();
35388     });
35389     // this should not be needed.. - it's actually the 'el' that resizes?
35390     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35391     
35392     //this.on('resize',  function (cp, w, h) {
35393     //        this.tree.innerCt.setWidth(w);
35394     //        this.tree.innerCt.setHeight(h);
35395     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35396     //});
35397
35398         
35399     
35400 };
35401
35402 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35403     fitToFrame : true,
35404     autoScroll : true
35405 });
35406
35407
35408
35409
35410
35411
35412
35413
35414
35415
35416
35417 /*
35418  * Based on:
35419  * Ext JS Library 1.1.1
35420  * Copyright(c) 2006-2007, Ext JS, LLC.
35421  *
35422  * Originally Released Under LGPL - original licence link has changed is not relivant.
35423  *
35424  * Fork - LGPL
35425  * <script type="text/javascript">
35426  */
35427  
35428
35429 /**
35430  * @class Roo.ReaderLayout
35431  * @extends Roo.BorderLayout
35432  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35433  * center region containing two nested regions (a top one for a list view and one for item preview below),
35434  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35435  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35436  * expedites the setup of the overall layout and regions for this common application style.
35437  * Example:
35438  <pre><code>
35439 var reader = new Roo.ReaderLayout();
35440 var CP = Roo.ContentPanel;  // shortcut for adding
35441
35442 reader.beginUpdate();
35443 reader.add("north", new CP("north", "North"));
35444 reader.add("west", new CP("west", {title: "West"}));
35445 reader.add("east", new CP("east", {title: "East"}));
35446
35447 reader.regions.listView.add(new CP("listView", "List"));
35448 reader.regions.preview.add(new CP("preview", "Preview"));
35449 reader.endUpdate();
35450 </code></pre>
35451 * @constructor
35452 * Create a new ReaderLayout
35453 * @param {Object} config Configuration options
35454 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35455 * document.body if omitted)
35456 */
35457 Roo.ReaderLayout = function(config, renderTo){
35458     var c = config || {size:{}};
35459     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35460         north: c.north !== false ? Roo.apply({
35461             split:false,
35462             initialSize: 32,
35463             titlebar: false
35464         }, c.north) : false,
35465         west: c.west !== false ? Roo.apply({
35466             split:true,
35467             initialSize: 200,
35468             minSize: 175,
35469             maxSize: 400,
35470             titlebar: true,
35471             collapsible: true,
35472             animate: true,
35473             margins:{left:5,right:0,bottom:5,top:5},
35474             cmargins:{left:5,right:5,bottom:5,top:5}
35475         }, c.west) : false,
35476         east: c.east !== false ? Roo.apply({
35477             split:true,
35478             initialSize: 200,
35479             minSize: 175,
35480             maxSize: 400,
35481             titlebar: true,
35482             collapsible: true,
35483             animate: true,
35484             margins:{left:0,right:5,bottom:5,top:5},
35485             cmargins:{left:5,right:5,bottom:5,top:5}
35486         }, c.east) : false,
35487         center: Roo.apply({
35488             tabPosition: 'top',
35489             autoScroll:false,
35490             closeOnTab: true,
35491             titlebar:false,
35492             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35493         }, c.center)
35494     });
35495
35496     this.el.addClass('x-reader');
35497
35498     this.beginUpdate();
35499
35500     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35501         south: c.preview !== false ? Roo.apply({
35502             split:true,
35503             initialSize: 200,
35504             minSize: 100,
35505             autoScroll:true,
35506             collapsible:true,
35507             titlebar: true,
35508             cmargins:{top:5,left:0, right:0, bottom:0}
35509         }, c.preview) : false,
35510         center: Roo.apply({
35511             autoScroll:false,
35512             titlebar:false,
35513             minHeight:200
35514         }, c.listView)
35515     });
35516     this.add('center', new Roo.NestedLayoutPanel(inner,
35517             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35518
35519     this.endUpdate();
35520
35521     this.regions.preview = inner.getRegion('south');
35522     this.regions.listView = inner.getRegion('center');
35523 };
35524
35525 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35526  * Based on:
35527  * Ext JS Library 1.1.1
35528  * Copyright(c) 2006-2007, Ext JS, LLC.
35529  *
35530  * Originally Released Under LGPL - original licence link has changed is not relivant.
35531  *
35532  * Fork - LGPL
35533  * <script type="text/javascript">
35534  */
35535  
35536 /**
35537  * @class Roo.grid.Grid
35538  * @extends Roo.util.Observable
35539  * This class represents the primary interface of a component based grid control.
35540  * <br><br>Usage:<pre><code>
35541  var grid = new Roo.grid.Grid("my-container-id", {
35542      ds: myDataStore,
35543      cm: myColModel,
35544      selModel: mySelectionModel,
35545      autoSizeColumns: true,
35546      monitorWindowResize: false,
35547      trackMouseOver: true
35548  });
35549  // set any options
35550  grid.render();
35551  * </code></pre>
35552  * <b>Common Problems:</b><br/>
35553  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35554  * element will correct this<br/>
35555  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35556  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35557  * are unpredictable.<br/>
35558  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35559  * grid to calculate dimensions/offsets.<br/>
35560   * @constructor
35561  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35562  * The container MUST have some type of size defined for the grid to fill. The container will be
35563  * automatically set to position relative if it isn't already.
35564  * @param {Object} config A config object that sets properties on this grid.
35565  */
35566 Roo.grid.Grid = function(container, config){
35567         // initialize the container
35568         this.container = Roo.get(container);
35569         this.container.update("");
35570         this.container.setStyle("overflow", "hidden");
35571     this.container.addClass('x-grid-container');
35572
35573     this.id = this.container.id;
35574
35575     Roo.apply(this, config);
35576     // check and correct shorthanded configs
35577     if(this.ds){
35578         this.dataSource = this.ds;
35579         delete this.ds;
35580     }
35581     if(this.cm){
35582         this.colModel = this.cm;
35583         delete this.cm;
35584     }
35585     if(this.sm){
35586         this.selModel = this.sm;
35587         delete this.sm;
35588     }
35589
35590     if (this.selModel) {
35591         this.selModel = Roo.factory(this.selModel, Roo.grid);
35592         this.sm = this.selModel;
35593         this.sm.xmodule = this.xmodule || false;
35594     }
35595     if (typeof(this.colModel.config) == 'undefined') {
35596         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35597         this.cm = this.colModel;
35598         this.cm.xmodule = this.xmodule || false;
35599     }
35600     if (this.dataSource) {
35601         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35602         this.ds = this.dataSource;
35603         this.ds.xmodule = this.xmodule || false;
35604          
35605     }
35606     
35607     
35608     
35609     if(this.width){
35610         this.container.setWidth(this.width);
35611     }
35612
35613     if(this.height){
35614         this.container.setHeight(this.height);
35615     }
35616     /** @private */
35617         this.addEvents({
35618         // raw events
35619         /**
35620          * @event click
35621          * The raw click event for the entire grid.
35622          * @param {Roo.EventObject} e
35623          */
35624         "click" : true,
35625         /**
35626          * @event dblclick
35627          * The raw dblclick event for the entire grid.
35628          * @param {Roo.EventObject} e
35629          */
35630         "dblclick" : true,
35631         /**
35632          * @event contextmenu
35633          * The raw contextmenu event for the entire grid.
35634          * @param {Roo.EventObject} e
35635          */
35636         "contextmenu" : true,
35637         /**
35638          * @event mousedown
35639          * The raw mousedown event for the entire grid.
35640          * @param {Roo.EventObject} e
35641          */
35642         "mousedown" : true,
35643         /**
35644          * @event mouseup
35645          * The raw mouseup event for the entire grid.
35646          * @param {Roo.EventObject} e
35647          */
35648         "mouseup" : true,
35649         /**
35650          * @event mouseover
35651          * The raw mouseover event for the entire grid.
35652          * @param {Roo.EventObject} e
35653          */
35654         "mouseover" : true,
35655         /**
35656          * @event mouseout
35657          * The raw mouseout event for the entire grid.
35658          * @param {Roo.EventObject} e
35659          */
35660         "mouseout" : true,
35661         /**
35662          * @event keypress
35663          * The raw keypress event for the entire grid.
35664          * @param {Roo.EventObject} e
35665          */
35666         "keypress" : true,
35667         /**
35668          * @event keydown
35669          * The raw keydown event for the entire grid.
35670          * @param {Roo.EventObject} e
35671          */
35672         "keydown" : true,
35673
35674         // custom events
35675
35676         /**
35677          * @event cellclick
35678          * Fires when a cell is clicked
35679          * @param {Grid} this
35680          * @param {Number} rowIndex
35681          * @param {Number} columnIndex
35682          * @param {Roo.EventObject} e
35683          */
35684         "cellclick" : true,
35685         /**
35686          * @event celldblclick
35687          * Fires when a cell is double clicked
35688          * @param {Grid} this
35689          * @param {Number} rowIndex
35690          * @param {Number} columnIndex
35691          * @param {Roo.EventObject} e
35692          */
35693         "celldblclick" : true,
35694         /**
35695          * @event rowclick
35696          * Fires when a row is clicked
35697          * @param {Grid} this
35698          * @param {Number} rowIndex
35699          * @param {Roo.EventObject} e
35700          */
35701         "rowclick" : true,
35702         /**
35703          * @event rowdblclick
35704          * Fires when a row is double clicked
35705          * @param {Grid} this
35706          * @param {Number} rowIndex
35707          * @param {Roo.EventObject} e
35708          */
35709         "rowdblclick" : true,
35710         /**
35711          * @event headerclick
35712          * Fires when a header is clicked
35713          * @param {Grid} this
35714          * @param {Number} columnIndex
35715          * @param {Roo.EventObject} e
35716          */
35717         "headerclick" : true,
35718         /**
35719          * @event headerdblclick
35720          * Fires when a header cell is double clicked
35721          * @param {Grid} this
35722          * @param {Number} columnIndex
35723          * @param {Roo.EventObject} e
35724          */
35725         "headerdblclick" : true,
35726         /**
35727          * @event rowcontextmenu
35728          * Fires when a row is right clicked
35729          * @param {Grid} this
35730          * @param {Number} rowIndex
35731          * @param {Roo.EventObject} e
35732          */
35733         "rowcontextmenu" : true,
35734         /**
35735          * @event cellcontextmenu
35736          * Fires when a cell is right clicked
35737          * @param {Grid} this
35738          * @param {Number} rowIndex
35739          * @param {Number} cellIndex
35740          * @param {Roo.EventObject} e
35741          */
35742          "cellcontextmenu" : true,
35743         /**
35744          * @event headercontextmenu
35745          * Fires when a header is right clicked
35746          * @param {Grid} this
35747          * @param {Number} columnIndex
35748          * @param {Roo.EventObject} e
35749          */
35750         "headercontextmenu" : true,
35751         /**
35752          * @event bodyscroll
35753          * Fires when the body element is scrolled
35754          * @param {Number} scrollLeft
35755          * @param {Number} scrollTop
35756          */
35757         "bodyscroll" : true,
35758         /**
35759          * @event columnresize
35760          * Fires when the user resizes a column
35761          * @param {Number} columnIndex
35762          * @param {Number} newSize
35763          */
35764         "columnresize" : true,
35765         /**
35766          * @event columnmove
35767          * Fires when the user moves a column
35768          * @param {Number} oldIndex
35769          * @param {Number} newIndex
35770          */
35771         "columnmove" : true,
35772         /**
35773          * @event startdrag
35774          * Fires when row(s) start being dragged
35775          * @param {Grid} this
35776          * @param {Roo.GridDD} dd The drag drop object
35777          * @param {event} e The raw browser event
35778          */
35779         "startdrag" : true,
35780         /**
35781          * @event enddrag
35782          * Fires when a drag operation is complete
35783          * @param {Grid} this
35784          * @param {Roo.GridDD} dd The drag drop object
35785          * @param {event} e The raw browser event
35786          */
35787         "enddrag" : true,
35788         /**
35789          * @event dragdrop
35790          * Fires when dragged row(s) are dropped on a valid DD target
35791          * @param {Grid} this
35792          * @param {Roo.GridDD} dd The drag drop object
35793          * @param {String} targetId The target drag drop object
35794          * @param {event} e The raw browser event
35795          */
35796         "dragdrop" : true,
35797         /**
35798          * @event dragover
35799          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35800          * @param {Grid} this
35801          * @param {Roo.GridDD} dd The drag drop object
35802          * @param {String} targetId The target drag drop object
35803          * @param {event} e The raw browser event
35804          */
35805         "dragover" : true,
35806         /**
35807          * @event dragenter
35808          *  Fires when the dragged row(s) first cross another DD target while being dragged
35809          * @param {Grid} this
35810          * @param {Roo.GridDD} dd The drag drop object
35811          * @param {String} targetId The target drag drop object
35812          * @param {event} e The raw browser event
35813          */
35814         "dragenter" : true,
35815         /**
35816          * @event dragout
35817          * Fires when the dragged row(s) leave another DD target while being dragged
35818          * @param {Grid} this
35819          * @param {Roo.GridDD} dd The drag drop object
35820          * @param {String} targetId The target drag drop object
35821          * @param {event} e The raw browser event
35822          */
35823         "dragout" : true,
35824         /**
35825          * @event rowclass
35826          * Fires when a row is rendered, so you can change add a style to it.
35827          * @param {GridView} gridview   The grid view
35828          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35829          */
35830         'rowclass' : true,
35831
35832         /**
35833          * @event render
35834          * Fires when the grid is rendered
35835          * @param {Grid} grid
35836          */
35837         'render' : true
35838     });
35839
35840     Roo.grid.Grid.superclass.constructor.call(this);
35841 };
35842 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35843     
35844     /**
35845      * @cfg {String} ddGroup - drag drop group.
35846      */
35847
35848     /**
35849      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35850      */
35851     minColumnWidth : 25,
35852
35853     /**
35854      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35855      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35856      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35857      */
35858     autoSizeColumns : false,
35859
35860     /**
35861      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35862      */
35863     autoSizeHeaders : true,
35864
35865     /**
35866      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35867      */
35868     monitorWindowResize : true,
35869
35870     /**
35871      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35872      * rows measured to get a columns size. Default is 0 (all rows).
35873      */
35874     maxRowsToMeasure : 0,
35875
35876     /**
35877      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35878      */
35879     trackMouseOver : true,
35880
35881     /**
35882     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35883     */
35884     
35885     /**
35886     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35887     */
35888     enableDragDrop : false,
35889     
35890     /**
35891     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35892     */
35893     enableColumnMove : true,
35894     
35895     /**
35896     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35897     */
35898     enableColumnHide : true,
35899     
35900     /**
35901     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35902     */
35903     enableRowHeightSync : false,
35904     
35905     /**
35906     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35907     */
35908     stripeRows : true,
35909     
35910     /**
35911     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35912     */
35913     autoHeight : false,
35914
35915     /**
35916      * @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.
35917      */
35918     autoExpandColumn : false,
35919
35920     /**
35921     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35922     * Default is 50.
35923     */
35924     autoExpandMin : 50,
35925
35926     /**
35927     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35928     */
35929     autoExpandMax : 1000,
35930
35931     /**
35932     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35933     */
35934     view : null,
35935
35936     /**
35937     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35938     */
35939     loadMask : false,
35940     /**
35941     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35942     */
35943     dropTarget: false,
35944     
35945    
35946     
35947     // private
35948     rendered : false,
35949
35950     /**
35951     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35952     * of a fixed width. Default is false.
35953     */
35954     /**
35955     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35956     */
35957     /**
35958      * Called once after all setup has been completed and the grid is ready to be rendered.
35959      * @return {Roo.grid.Grid} this
35960      */
35961     render : function()
35962     {
35963         var c = this.container;
35964         // try to detect autoHeight/width mode
35965         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35966             this.autoHeight = true;
35967         }
35968         var view = this.getView();
35969         view.init(this);
35970
35971         c.on("click", this.onClick, this);
35972         c.on("dblclick", this.onDblClick, this);
35973         c.on("contextmenu", this.onContextMenu, this);
35974         c.on("keydown", this.onKeyDown, this);
35975         if (Roo.isTouch) {
35976             c.on("touchstart", this.onTouchStart, this);
35977         }
35978
35979         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35980
35981         this.getSelectionModel().init(this);
35982
35983         view.render();
35984
35985         if(this.loadMask){
35986             this.loadMask = new Roo.LoadMask(this.container,
35987                     Roo.apply({store:this.dataSource}, this.loadMask));
35988         }
35989         
35990         
35991         if (this.toolbar && this.toolbar.xtype) {
35992             this.toolbar.container = this.getView().getHeaderPanel(true);
35993             this.toolbar = new Roo.Toolbar(this.toolbar);
35994         }
35995         if (this.footer && this.footer.xtype) {
35996             this.footer.dataSource = this.getDataSource();
35997             this.footer.container = this.getView().getFooterPanel(true);
35998             this.footer = Roo.factory(this.footer, Roo);
35999         }
36000         if (this.dropTarget && this.dropTarget.xtype) {
36001             delete this.dropTarget.xtype;
36002             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36003         }
36004         
36005         
36006         this.rendered = true;
36007         this.fireEvent('render', this);
36008         return this;
36009     },
36010
36011         /**
36012          * Reconfigures the grid to use a different Store and Column Model.
36013          * The View will be bound to the new objects and refreshed.
36014          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36015          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36016          */
36017     reconfigure : function(dataSource, colModel){
36018         if(this.loadMask){
36019             this.loadMask.destroy();
36020             this.loadMask = new Roo.LoadMask(this.container,
36021                     Roo.apply({store:dataSource}, this.loadMask));
36022         }
36023         this.view.bind(dataSource, colModel);
36024         this.dataSource = dataSource;
36025         this.colModel = colModel;
36026         this.view.refresh(true);
36027     },
36028
36029     // private
36030     onKeyDown : function(e){
36031         this.fireEvent("keydown", e);
36032     },
36033
36034     /**
36035      * Destroy this grid.
36036      * @param {Boolean} removeEl True to remove the element
36037      */
36038     destroy : function(removeEl, keepListeners){
36039         if(this.loadMask){
36040             this.loadMask.destroy();
36041         }
36042         var c = this.container;
36043         c.removeAllListeners();
36044         this.view.destroy();
36045         this.colModel.purgeListeners();
36046         if(!keepListeners){
36047             this.purgeListeners();
36048         }
36049         c.update("");
36050         if(removeEl === true){
36051             c.remove();
36052         }
36053     },
36054
36055     // private
36056     processEvent : function(name, e){
36057         // does this fire select???
36058         Roo.log('grid:processEvent '  + name);
36059         
36060         if (name != 'touchstart' ) {
36061             this.fireEvent(name, e);    
36062         }
36063         
36064         var t = e.getTarget();
36065         var v = this.view;
36066         var header = v.findHeaderIndex(t);
36067         if(header !== false){
36068             var ename = name == 'touchstart' ? 'click' : name;
36069              
36070             this.fireEvent("header" + ename, this, header, e);
36071         }else{
36072             var row = v.findRowIndex(t);
36073             var cell = v.findCellIndex(t);
36074             if (name == 'touchstart') {
36075                 // first touch is always a click.
36076                 // hopefull this happens after selection is updated.?
36077                 name = false;
36078                 
36079                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36080                     var cs = this.selModel.getSelectedCell();
36081                     if (row == cs[0] && cell == cs[1]){
36082                         name = 'dblclick';
36083                     }
36084                 }
36085                 if (typeof(this.selModel.getSelections) != 'undefined') {
36086                     var cs = this.selModel.getSelections();
36087                     var ds = this.dataSource;
36088                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36089                         name = 'dblclick';
36090                     }
36091                 }
36092                 if (!name) {
36093                     return;
36094                 }
36095             }
36096             
36097             
36098             if(row !== false){
36099                 this.fireEvent("row" + name, this, row, e);
36100                 if(cell !== false){
36101                     this.fireEvent("cell" + name, this, row, cell, e);
36102                 }
36103             }
36104         }
36105     },
36106
36107     // private
36108     onClick : function(e){
36109         this.processEvent("click", e);
36110     },
36111    // private
36112     onTouchStart : function(e){
36113         this.processEvent("touchstart", e);
36114     },
36115
36116     // private
36117     onContextMenu : function(e, t){
36118         this.processEvent("contextmenu", e);
36119     },
36120
36121     // private
36122     onDblClick : function(e){
36123         this.processEvent("dblclick", e);
36124     },
36125
36126     // private
36127     walkCells : function(row, col, step, fn, scope){
36128         var cm = this.colModel, clen = cm.getColumnCount();
36129         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36130         if(step < 0){
36131             if(col < 0){
36132                 row--;
36133                 first = false;
36134             }
36135             while(row >= 0){
36136                 if(!first){
36137                     col = clen-1;
36138                 }
36139                 first = false;
36140                 while(col >= 0){
36141                     if(fn.call(scope || this, row, col, cm) === true){
36142                         return [row, col];
36143                     }
36144                     col--;
36145                 }
36146                 row--;
36147             }
36148         } else {
36149             if(col >= clen){
36150                 row++;
36151                 first = false;
36152             }
36153             while(row < rlen){
36154                 if(!first){
36155                     col = 0;
36156                 }
36157                 first = false;
36158                 while(col < clen){
36159                     if(fn.call(scope || this, row, col, cm) === true){
36160                         return [row, col];
36161                     }
36162                     col++;
36163                 }
36164                 row++;
36165             }
36166         }
36167         return null;
36168     },
36169
36170     // private
36171     getSelections : function(){
36172         return this.selModel.getSelections();
36173     },
36174
36175     /**
36176      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36177      * but if manual update is required this method will initiate it.
36178      */
36179     autoSize : function(){
36180         if(this.rendered){
36181             this.view.layout();
36182             if(this.view.adjustForScroll){
36183                 this.view.adjustForScroll();
36184             }
36185         }
36186     },
36187
36188     /**
36189      * Returns the grid's underlying element.
36190      * @return {Element} The element
36191      */
36192     getGridEl : function(){
36193         return this.container;
36194     },
36195
36196     // private for compatibility, overridden by editor grid
36197     stopEditing : function(){},
36198
36199     /**
36200      * Returns the grid's SelectionModel.
36201      * @return {SelectionModel}
36202      */
36203     getSelectionModel : function(){
36204         if(!this.selModel){
36205             this.selModel = new Roo.grid.RowSelectionModel();
36206         }
36207         return this.selModel;
36208     },
36209
36210     /**
36211      * Returns the grid's DataSource.
36212      * @return {DataSource}
36213      */
36214     getDataSource : function(){
36215         return this.dataSource;
36216     },
36217
36218     /**
36219      * Returns the grid's ColumnModel.
36220      * @return {ColumnModel}
36221      */
36222     getColumnModel : function(){
36223         return this.colModel;
36224     },
36225
36226     /**
36227      * Returns the grid's GridView object.
36228      * @return {GridView}
36229      */
36230     getView : function(){
36231         if(!this.view){
36232             this.view = new Roo.grid.GridView(this.viewConfig);
36233         }
36234         return this.view;
36235     },
36236     /**
36237      * Called to get grid's drag proxy text, by default returns this.ddText.
36238      * @return {String}
36239      */
36240     getDragDropText : function(){
36241         var count = this.selModel.getCount();
36242         return String.format(this.ddText, count, count == 1 ? '' : 's');
36243     }
36244 });
36245 /**
36246  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36247  * %0 is replaced with the number of selected rows.
36248  * @type String
36249  */
36250 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36251  * Based on:
36252  * Ext JS Library 1.1.1
36253  * Copyright(c) 2006-2007, Ext JS, LLC.
36254  *
36255  * Originally Released Under LGPL - original licence link has changed is not relivant.
36256  *
36257  * Fork - LGPL
36258  * <script type="text/javascript">
36259  */
36260  
36261 Roo.grid.AbstractGridView = function(){
36262         this.grid = null;
36263         
36264         this.events = {
36265             "beforerowremoved" : true,
36266             "beforerowsinserted" : true,
36267             "beforerefresh" : true,
36268             "rowremoved" : true,
36269             "rowsinserted" : true,
36270             "rowupdated" : true,
36271             "refresh" : true
36272         };
36273     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36274 };
36275
36276 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36277     rowClass : "x-grid-row",
36278     cellClass : "x-grid-cell",
36279     tdClass : "x-grid-td",
36280     hdClass : "x-grid-hd",
36281     splitClass : "x-grid-hd-split",
36282     
36283     init: function(grid){
36284         this.grid = grid;
36285                 var cid = this.grid.getGridEl().id;
36286         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36287         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36288         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36289         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36290         },
36291         
36292     getColumnRenderers : function(){
36293         var renderers = [];
36294         var cm = this.grid.colModel;
36295         var colCount = cm.getColumnCount();
36296         for(var i = 0; i < colCount; i++){
36297             renderers[i] = cm.getRenderer(i);
36298         }
36299         return renderers;
36300     },
36301     
36302     getColumnIds : function(){
36303         var ids = [];
36304         var cm = this.grid.colModel;
36305         var colCount = cm.getColumnCount();
36306         for(var i = 0; i < colCount; i++){
36307             ids[i] = cm.getColumnId(i);
36308         }
36309         return ids;
36310     },
36311     
36312     getDataIndexes : function(){
36313         if(!this.indexMap){
36314             this.indexMap = this.buildIndexMap();
36315         }
36316         return this.indexMap.colToData;
36317     },
36318     
36319     getColumnIndexByDataIndex : function(dataIndex){
36320         if(!this.indexMap){
36321             this.indexMap = this.buildIndexMap();
36322         }
36323         return this.indexMap.dataToCol[dataIndex];
36324     },
36325     
36326     /**
36327      * Set a css style for a column dynamically. 
36328      * @param {Number} colIndex The index of the column
36329      * @param {String} name The css property name
36330      * @param {String} value The css value
36331      */
36332     setCSSStyle : function(colIndex, name, value){
36333         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36334         Roo.util.CSS.updateRule(selector, name, value);
36335     },
36336     
36337     generateRules : function(cm){
36338         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36339         Roo.util.CSS.removeStyleSheet(rulesId);
36340         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36341             var cid = cm.getColumnId(i);
36342             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36343                          this.tdSelector, cid, " {\n}\n",
36344                          this.hdSelector, cid, " {\n}\n",
36345                          this.splitSelector, cid, " {\n}\n");
36346         }
36347         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36348     }
36349 });/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359
36360 // private
36361 // This is a support class used internally by the Grid components
36362 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36363     this.grid = grid;
36364     this.view = grid.getView();
36365     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36366     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36367     if(hd2){
36368         this.setHandleElId(Roo.id(hd));
36369         this.setOuterHandleElId(Roo.id(hd2));
36370     }
36371     this.scroll = false;
36372 };
36373 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36374     maxDragWidth: 120,
36375     getDragData : function(e){
36376         var t = Roo.lib.Event.getTarget(e);
36377         var h = this.view.findHeaderCell(t);
36378         if(h){
36379             return {ddel: h.firstChild, header:h};
36380         }
36381         return false;
36382     },
36383
36384     onInitDrag : function(e){
36385         this.view.headersDisabled = true;
36386         var clone = this.dragData.ddel.cloneNode(true);
36387         clone.id = Roo.id();
36388         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36389         this.proxy.update(clone);
36390         return true;
36391     },
36392
36393     afterValidDrop : function(){
36394         var v = this.view;
36395         setTimeout(function(){
36396             v.headersDisabled = false;
36397         }, 50);
36398     },
36399
36400     afterInvalidDrop : function(){
36401         var v = this.view;
36402         setTimeout(function(){
36403             v.headersDisabled = false;
36404         }, 50);
36405     }
36406 });
36407 /*
36408  * Based on:
36409  * Ext JS Library 1.1.1
36410  * Copyright(c) 2006-2007, Ext JS, LLC.
36411  *
36412  * Originally Released Under LGPL - original licence link has changed is not relivant.
36413  *
36414  * Fork - LGPL
36415  * <script type="text/javascript">
36416  */
36417 // private
36418 // This is a support class used internally by the Grid components
36419 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36420     this.grid = grid;
36421     this.view = grid.getView();
36422     // split the proxies so they don't interfere with mouse events
36423     this.proxyTop = Roo.DomHelper.append(document.body, {
36424         cls:"col-move-top", html:"&#160;"
36425     }, true);
36426     this.proxyBottom = Roo.DomHelper.append(document.body, {
36427         cls:"col-move-bottom", html:"&#160;"
36428     }, true);
36429     this.proxyTop.hide = this.proxyBottom.hide = function(){
36430         this.setLeftTop(-100,-100);
36431         this.setStyle("visibility", "hidden");
36432     };
36433     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36434     // temporarily disabled
36435     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36436     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36437 };
36438 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36439     proxyOffsets : [-4, -9],
36440     fly: Roo.Element.fly,
36441
36442     getTargetFromEvent : function(e){
36443         var t = Roo.lib.Event.getTarget(e);
36444         var cindex = this.view.findCellIndex(t);
36445         if(cindex !== false){
36446             return this.view.getHeaderCell(cindex);
36447         }
36448         return null;
36449     },
36450
36451     nextVisible : function(h){
36452         var v = this.view, cm = this.grid.colModel;
36453         h = h.nextSibling;
36454         while(h){
36455             if(!cm.isHidden(v.getCellIndex(h))){
36456                 return h;
36457             }
36458             h = h.nextSibling;
36459         }
36460         return null;
36461     },
36462
36463     prevVisible : function(h){
36464         var v = this.view, cm = this.grid.colModel;
36465         h = h.prevSibling;
36466         while(h){
36467             if(!cm.isHidden(v.getCellIndex(h))){
36468                 return h;
36469             }
36470             h = h.prevSibling;
36471         }
36472         return null;
36473     },
36474
36475     positionIndicator : function(h, n, e){
36476         var x = Roo.lib.Event.getPageX(e);
36477         var r = Roo.lib.Dom.getRegion(n.firstChild);
36478         var px, pt, py = r.top + this.proxyOffsets[1];
36479         if((r.right - x) <= (r.right-r.left)/2){
36480             px = r.right+this.view.borderWidth;
36481             pt = "after";
36482         }else{
36483             px = r.left;
36484             pt = "before";
36485         }
36486         var oldIndex = this.view.getCellIndex(h);
36487         var newIndex = this.view.getCellIndex(n);
36488
36489         if(this.grid.colModel.isFixed(newIndex)){
36490             return false;
36491         }
36492
36493         var locked = this.grid.colModel.isLocked(newIndex);
36494
36495         if(pt == "after"){
36496             newIndex++;
36497         }
36498         if(oldIndex < newIndex){
36499             newIndex--;
36500         }
36501         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36502             return false;
36503         }
36504         px +=  this.proxyOffsets[0];
36505         this.proxyTop.setLeftTop(px, py);
36506         this.proxyTop.show();
36507         if(!this.bottomOffset){
36508             this.bottomOffset = this.view.mainHd.getHeight();
36509         }
36510         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36511         this.proxyBottom.show();
36512         return pt;
36513     },
36514
36515     onNodeEnter : function(n, dd, e, data){
36516         if(data.header != n){
36517             this.positionIndicator(data.header, n, e);
36518         }
36519     },
36520
36521     onNodeOver : function(n, dd, e, data){
36522         var result = false;
36523         if(data.header != n){
36524             result = this.positionIndicator(data.header, n, e);
36525         }
36526         if(!result){
36527             this.proxyTop.hide();
36528             this.proxyBottom.hide();
36529         }
36530         return result ? this.dropAllowed : this.dropNotAllowed;
36531     },
36532
36533     onNodeOut : function(n, dd, e, data){
36534         this.proxyTop.hide();
36535         this.proxyBottom.hide();
36536     },
36537
36538     onNodeDrop : function(n, dd, e, data){
36539         var h = data.header;
36540         if(h != n){
36541             var cm = this.grid.colModel;
36542             var x = Roo.lib.Event.getPageX(e);
36543             var r = Roo.lib.Dom.getRegion(n.firstChild);
36544             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36545             var oldIndex = this.view.getCellIndex(h);
36546             var newIndex = this.view.getCellIndex(n);
36547             var locked = cm.isLocked(newIndex);
36548             if(pt == "after"){
36549                 newIndex++;
36550             }
36551             if(oldIndex < newIndex){
36552                 newIndex--;
36553             }
36554             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36555                 return false;
36556             }
36557             cm.setLocked(oldIndex, locked, true);
36558             cm.moveColumn(oldIndex, newIndex);
36559             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36560             return true;
36561         }
36562         return false;
36563     }
36564 });
36565 /*
36566  * Based on:
36567  * Ext JS Library 1.1.1
36568  * Copyright(c) 2006-2007, Ext JS, LLC.
36569  *
36570  * Originally Released Under LGPL - original licence link has changed is not relivant.
36571  *
36572  * Fork - LGPL
36573  * <script type="text/javascript">
36574  */
36575   
36576 /**
36577  * @class Roo.grid.GridView
36578  * @extends Roo.util.Observable
36579  *
36580  * @constructor
36581  * @param {Object} config
36582  */
36583 Roo.grid.GridView = function(config){
36584     Roo.grid.GridView.superclass.constructor.call(this);
36585     this.el = null;
36586
36587     Roo.apply(this, config);
36588 };
36589
36590 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36591
36592     unselectable :  'unselectable="on"',
36593     unselectableCls :  'x-unselectable',
36594     
36595     
36596     rowClass : "x-grid-row",
36597
36598     cellClass : "x-grid-col",
36599
36600     tdClass : "x-grid-td",
36601
36602     hdClass : "x-grid-hd",
36603
36604     splitClass : "x-grid-split",
36605
36606     sortClasses : ["sort-asc", "sort-desc"],
36607
36608     enableMoveAnim : false,
36609
36610     hlColor: "C3DAF9",
36611
36612     dh : Roo.DomHelper,
36613
36614     fly : Roo.Element.fly,
36615
36616     css : Roo.util.CSS,
36617
36618     borderWidth: 1,
36619
36620     splitOffset: 3,
36621
36622     scrollIncrement : 22,
36623
36624     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36625
36626     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36627
36628     bind : function(ds, cm){
36629         if(this.ds){
36630             this.ds.un("load", this.onLoad, this);
36631             this.ds.un("datachanged", this.onDataChange, this);
36632             this.ds.un("add", this.onAdd, this);
36633             this.ds.un("remove", this.onRemove, this);
36634             this.ds.un("update", this.onUpdate, this);
36635             this.ds.un("clear", this.onClear, this);
36636         }
36637         if(ds){
36638             ds.on("load", this.onLoad, this);
36639             ds.on("datachanged", this.onDataChange, this);
36640             ds.on("add", this.onAdd, this);
36641             ds.on("remove", this.onRemove, this);
36642             ds.on("update", this.onUpdate, this);
36643             ds.on("clear", this.onClear, this);
36644         }
36645         this.ds = ds;
36646
36647         if(this.cm){
36648             this.cm.un("widthchange", this.onColWidthChange, this);
36649             this.cm.un("headerchange", this.onHeaderChange, this);
36650             this.cm.un("hiddenchange", this.onHiddenChange, this);
36651             this.cm.un("columnmoved", this.onColumnMove, this);
36652             this.cm.un("columnlockchange", this.onColumnLock, this);
36653         }
36654         if(cm){
36655             this.generateRules(cm);
36656             cm.on("widthchange", this.onColWidthChange, this);
36657             cm.on("headerchange", this.onHeaderChange, this);
36658             cm.on("hiddenchange", this.onHiddenChange, this);
36659             cm.on("columnmoved", this.onColumnMove, this);
36660             cm.on("columnlockchange", this.onColumnLock, this);
36661         }
36662         this.cm = cm;
36663     },
36664
36665     init: function(grid){
36666         Roo.grid.GridView.superclass.init.call(this, grid);
36667
36668         this.bind(grid.dataSource, grid.colModel);
36669
36670         grid.on("headerclick", this.handleHeaderClick, this);
36671
36672         if(grid.trackMouseOver){
36673             grid.on("mouseover", this.onRowOver, this);
36674             grid.on("mouseout", this.onRowOut, this);
36675         }
36676         grid.cancelTextSelection = function(){};
36677         this.gridId = grid.id;
36678
36679         var tpls = this.templates || {};
36680
36681         if(!tpls.master){
36682             tpls.master = new Roo.Template(
36683                '<div class="x-grid" hidefocus="true">',
36684                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36685                   '<div class="x-grid-topbar"></div>',
36686                   '<div class="x-grid-scroller"><div></div></div>',
36687                   '<div class="x-grid-locked">',
36688                       '<div class="x-grid-header">{lockedHeader}</div>',
36689                       '<div class="x-grid-body">{lockedBody}</div>',
36690                   "</div>",
36691                   '<div class="x-grid-viewport">',
36692                       '<div class="x-grid-header">{header}</div>',
36693                       '<div class="x-grid-body">{body}</div>',
36694                   "</div>",
36695                   '<div class="x-grid-bottombar"></div>',
36696                  
36697                   '<div class="x-grid-resize-proxy">&#160;</div>',
36698                "</div>"
36699             );
36700             tpls.master.disableformats = true;
36701         }
36702
36703         if(!tpls.header){
36704             tpls.header = new Roo.Template(
36705                '<table border="0" cellspacing="0" cellpadding="0">',
36706                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36707                "</table>{splits}"
36708             );
36709             tpls.header.disableformats = true;
36710         }
36711         tpls.header.compile();
36712
36713         if(!tpls.hcell){
36714             tpls.hcell = new Roo.Template(
36715                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36716                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36717                 "</div></td>"
36718              );
36719              tpls.hcell.disableFormats = true;
36720         }
36721         tpls.hcell.compile();
36722
36723         if(!tpls.hsplit){
36724             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36725                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36726             tpls.hsplit.disableFormats = true;
36727         }
36728         tpls.hsplit.compile();
36729
36730         if(!tpls.body){
36731             tpls.body = new Roo.Template(
36732                '<table border="0" cellspacing="0" cellpadding="0">',
36733                "<tbody>{rows}</tbody>",
36734                "</table>"
36735             );
36736             tpls.body.disableFormats = true;
36737         }
36738         tpls.body.compile();
36739
36740         if(!tpls.row){
36741             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36742             tpls.row.disableFormats = true;
36743         }
36744         tpls.row.compile();
36745
36746         if(!tpls.cell){
36747             tpls.cell = new Roo.Template(
36748                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36749                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36750                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36751                 "</td>"
36752             );
36753             tpls.cell.disableFormats = true;
36754         }
36755         tpls.cell.compile();
36756
36757         this.templates = tpls;
36758     },
36759
36760     // remap these for backwards compat
36761     onColWidthChange : function(){
36762         this.updateColumns.apply(this, arguments);
36763     },
36764     onHeaderChange : function(){
36765         this.updateHeaders.apply(this, arguments);
36766     }, 
36767     onHiddenChange : function(){
36768         this.handleHiddenChange.apply(this, arguments);
36769     },
36770     onColumnMove : function(){
36771         this.handleColumnMove.apply(this, arguments);
36772     },
36773     onColumnLock : function(){
36774         this.handleLockChange.apply(this, arguments);
36775     },
36776
36777     onDataChange : function(){
36778         this.refresh();
36779         this.updateHeaderSortState();
36780     },
36781
36782     onClear : function(){
36783         this.refresh();
36784     },
36785
36786     onUpdate : function(ds, record){
36787         this.refreshRow(record);
36788     },
36789
36790     refreshRow : function(record){
36791         var ds = this.ds, index;
36792         if(typeof record == 'number'){
36793             index = record;
36794             record = ds.getAt(index);
36795         }else{
36796             index = ds.indexOf(record);
36797         }
36798         this.insertRows(ds, index, index, true);
36799         this.onRemove(ds, record, index+1, true);
36800         this.syncRowHeights(index, index);
36801         this.layout();
36802         this.fireEvent("rowupdated", this, index, record);
36803     },
36804
36805     onAdd : function(ds, records, index){
36806         this.insertRows(ds, index, index + (records.length-1));
36807     },
36808
36809     onRemove : function(ds, record, index, isUpdate){
36810         if(isUpdate !== true){
36811             this.fireEvent("beforerowremoved", this, index, record);
36812         }
36813         var bt = this.getBodyTable(), lt = this.getLockedTable();
36814         if(bt.rows[index]){
36815             bt.firstChild.removeChild(bt.rows[index]);
36816         }
36817         if(lt.rows[index]){
36818             lt.firstChild.removeChild(lt.rows[index]);
36819         }
36820         if(isUpdate !== true){
36821             this.stripeRows(index);
36822             this.syncRowHeights(index, index);
36823             this.layout();
36824             this.fireEvent("rowremoved", this, index, record);
36825         }
36826     },
36827
36828     onLoad : function(){
36829         this.scrollToTop();
36830     },
36831
36832     /**
36833      * Scrolls the grid to the top
36834      */
36835     scrollToTop : function(){
36836         if(this.scroller){
36837             this.scroller.dom.scrollTop = 0;
36838             this.syncScroll();
36839         }
36840     },
36841
36842     /**
36843      * Gets a panel in the header of the grid that can be used for toolbars etc.
36844      * After modifying the contents of this panel a call to grid.autoSize() may be
36845      * required to register any changes in size.
36846      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36847      * @return Roo.Element
36848      */
36849     getHeaderPanel : function(doShow){
36850         if(doShow){
36851             this.headerPanel.show();
36852         }
36853         return this.headerPanel;
36854     },
36855
36856     /**
36857      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36858      * After modifying the contents of this panel a call to grid.autoSize() may be
36859      * required to register any changes in size.
36860      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36861      * @return Roo.Element
36862      */
36863     getFooterPanel : function(doShow){
36864         if(doShow){
36865             this.footerPanel.show();
36866         }
36867         return this.footerPanel;
36868     },
36869
36870     initElements : function(){
36871         var E = Roo.Element;
36872         var el = this.grid.getGridEl().dom.firstChild;
36873         var cs = el.childNodes;
36874
36875         this.el = new E(el);
36876         
36877          this.focusEl = new E(el.firstChild);
36878         this.focusEl.swallowEvent("click", true);
36879         
36880         this.headerPanel = new E(cs[1]);
36881         this.headerPanel.enableDisplayMode("block");
36882
36883         this.scroller = new E(cs[2]);
36884         this.scrollSizer = new E(this.scroller.dom.firstChild);
36885
36886         this.lockedWrap = new E(cs[3]);
36887         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36888         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36889
36890         this.mainWrap = new E(cs[4]);
36891         this.mainHd = new E(this.mainWrap.dom.firstChild);
36892         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36893
36894         this.footerPanel = new E(cs[5]);
36895         this.footerPanel.enableDisplayMode("block");
36896
36897         this.resizeProxy = new E(cs[6]);
36898
36899         this.headerSelector = String.format(
36900            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36901            this.lockedHd.id, this.mainHd.id
36902         );
36903
36904         this.splitterSelector = String.format(
36905            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36906            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36907         );
36908     },
36909     idToCssName : function(s)
36910     {
36911         return s.replace(/[^a-z0-9]+/ig, '-');
36912     },
36913
36914     getHeaderCell : function(index){
36915         return Roo.DomQuery.select(this.headerSelector)[index];
36916     },
36917
36918     getHeaderCellMeasure : function(index){
36919         return this.getHeaderCell(index).firstChild;
36920     },
36921
36922     getHeaderCellText : function(index){
36923         return this.getHeaderCell(index).firstChild.firstChild;
36924     },
36925
36926     getLockedTable : function(){
36927         return this.lockedBody.dom.firstChild;
36928     },
36929
36930     getBodyTable : function(){
36931         return this.mainBody.dom.firstChild;
36932     },
36933
36934     getLockedRow : function(index){
36935         return this.getLockedTable().rows[index];
36936     },
36937
36938     getRow : function(index){
36939         return this.getBodyTable().rows[index];
36940     },
36941
36942     getRowComposite : function(index){
36943         if(!this.rowEl){
36944             this.rowEl = new Roo.CompositeElementLite();
36945         }
36946         var els = [], lrow, mrow;
36947         if(lrow = this.getLockedRow(index)){
36948             els.push(lrow);
36949         }
36950         if(mrow = this.getRow(index)){
36951             els.push(mrow);
36952         }
36953         this.rowEl.elements = els;
36954         return this.rowEl;
36955     },
36956     /**
36957      * Gets the 'td' of the cell
36958      * 
36959      * @param {Integer} rowIndex row to select
36960      * @param {Integer} colIndex column to select
36961      * 
36962      * @return {Object} 
36963      */
36964     getCell : function(rowIndex, colIndex){
36965         var locked = this.cm.getLockedCount();
36966         var source;
36967         if(colIndex < locked){
36968             source = this.lockedBody.dom.firstChild;
36969         }else{
36970             source = this.mainBody.dom.firstChild;
36971             colIndex -= locked;
36972         }
36973         return source.rows[rowIndex].childNodes[colIndex];
36974     },
36975
36976     getCellText : function(rowIndex, colIndex){
36977         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36978     },
36979
36980     getCellBox : function(cell){
36981         var b = this.fly(cell).getBox();
36982         if(Roo.isOpera){ // opera fails to report the Y
36983             b.y = cell.offsetTop + this.mainBody.getY();
36984         }
36985         return b;
36986     },
36987
36988     getCellIndex : function(cell){
36989         var id = String(cell.className).match(this.cellRE);
36990         if(id){
36991             return parseInt(id[1], 10);
36992         }
36993         return 0;
36994     },
36995
36996     findHeaderIndex : function(n){
36997         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36998         return r ? this.getCellIndex(r) : false;
36999     },
37000
37001     findHeaderCell : function(n){
37002         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37003         return r ? r : false;
37004     },
37005
37006     findRowIndex : function(n){
37007         if(!n){
37008             return false;
37009         }
37010         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37011         return r ? r.rowIndex : false;
37012     },
37013
37014     findCellIndex : function(node){
37015         var stop = this.el.dom;
37016         while(node && node != stop){
37017             if(this.findRE.test(node.className)){
37018                 return this.getCellIndex(node);
37019             }
37020             node = node.parentNode;
37021         }
37022         return false;
37023     },
37024
37025     getColumnId : function(index){
37026         return this.cm.getColumnId(index);
37027     },
37028
37029     getSplitters : function()
37030     {
37031         if(this.splitterSelector){
37032            return Roo.DomQuery.select(this.splitterSelector);
37033         }else{
37034             return null;
37035       }
37036     },
37037
37038     getSplitter : function(index){
37039         return this.getSplitters()[index];
37040     },
37041
37042     onRowOver : function(e, t){
37043         var row;
37044         if((row = this.findRowIndex(t)) !== false){
37045             this.getRowComposite(row).addClass("x-grid-row-over");
37046         }
37047     },
37048
37049     onRowOut : function(e, t){
37050         var row;
37051         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37052             this.getRowComposite(row).removeClass("x-grid-row-over");
37053         }
37054     },
37055
37056     renderHeaders : function(){
37057         var cm = this.cm;
37058         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37059         var cb = [], lb = [], sb = [], lsb = [], p = {};
37060         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37061             p.cellId = "x-grid-hd-0-" + i;
37062             p.splitId = "x-grid-csplit-0-" + i;
37063             p.id = cm.getColumnId(i);
37064             p.title = cm.getColumnTooltip(i) || "";
37065             p.value = cm.getColumnHeader(i) || "";
37066             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37067             if(!cm.isLocked(i)){
37068                 cb[cb.length] = ct.apply(p);
37069                 sb[sb.length] = st.apply(p);
37070             }else{
37071                 lb[lb.length] = ct.apply(p);
37072                 lsb[lsb.length] = st.apply(p);
37073             }
37074         }
37075         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37076                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37077     },
37078
37079     updateHeaders : function(){
37080         var html = this.renderHeaders();
37081         this.lockedHd.update(html[0]);
37082         this.mainHd.update(html[1]);
37083     },
37084
37085     /**
37086      * Focuses the specified row.
37087      * @param {Number} row The row index
37088      */
37089     focusRow : function(row)
37090     {
37091         //Roo.log('GridView.focusRow');
37092         var x = this.scroller.dom.scrollLeft;
37093         this.focusCell(row, 0, false);
37094         this.scroller.dom.scrollLeft = x;
37095     },
37096
37097     /**
37098      * Focuses the specified cell.
37099      * @param {Number} row The row index
37100      * @param {Number} col The column index
37101      * @param {Boolean} hscroll false to disable horizontal scrolling
37102      */
37103     focusCell : function(row, col, hscroll)
37104     {
37105         //Roo.log('GridView.focusCell');
37106         var el = this.ensureVisible(row, col, hscroll);
37107         this.focusEl.alignTo(el, "tl-tl");
37108         if(Roo.isGecko){
37109             this.focusEl.focus();
37110         }else{
37111             this.focusEl.focus.defer(1, this.focusEl);
37112         }
37113     },
37114
37115     /**
37116      * Scrolls the specified cell into view
37117      * @param {Number} row The row index
37118      * @param {Number} col The column index
37119      * @param {Boolean} hscroll false to disable horizontal scrolling
37120      */
37121     ensureVisible : function(row, col, hscroll)
37122     {
37123         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37124         //return null; //disable for testing.
37125         if(typeof row != "number"){
37126             row = row.rowIndex;
37127         }
37128         if(row < 0 && row >= this.ds.getCount()){
37129             return  null;
37130         }
37131         col = (col !== undefined ? col : 0);
37132         var cm = this.grid.colModel;
37133         while(cm.isHidden(col)){
37134             col++;
37135         }
37136
37137         var el = this.getCell(row, col);
37138         if(!el){
37139             return null;
37140         }
37141         var c = this.scroller.dom;
37142
37143         var ctop = parseInt(el.offsetTop, 10);
37144         var cleft = parseInt(el.offsetLeft, 10);
37145         var cbot = ctop + el.offsetHeight;
37146         var cright = cleft + el.offsetWidth;
37147         
37148         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37149         var stop = parseInt(c.scrollTop, 10);
37150         var sleft = parseInt(c.scrollLeft, 10);
37151         var sbot = stop + ch;
37152         var sright = sleft + c.clientWidth;
37153         /*
37154         Roo.log('GridView.ensureVisible:' +
37155                 ' ctop:' + ctop +
37156                 ' c.clientHeight:' + c.clientHeight +
37157                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37158                 ' stop:' + stop +
37159                 ' cbot:' + cbot +
37160                 ' sbot:' + sbot +
37161                 ' ch:' + ch  
37162                 );
37163         */
37164         if(ctop < stop){
37165              c.scrollTop = ctop;
37166             //Roo.log("set scrolltop to ctop DISABLE?");
37167         }else if(cbot > sbot){
37168             //Roo.log("set scrolltop to cbot-ch");
37169             c.scrollTop = cbot-ch;
37170         }
37171         
37172         if(hscroll !== false){
37173             if(cleft < sleft){
37174                 c.scrollLeft = cleft;
37175             }else if(cright > sright){
37176                 c.scrollLeft = cright-c.clientWidth;
37177             }
37178         }
37179          
37180         return el;
37181     },
37182
37183     updateColumns : function(){
37184         this.grid.stopEditing();
37185         var cm = this.grid.colModel, colIds = this.getColumnIds();
37186         //var totalWidth = cm.getTotalWidth();
37187         var pos = 0;
37188         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37189             //if(cm.isHidden(i)) continue;
37190             var w = cm.getColumnWidth(i);
37191             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37192             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37193         }
37194         this.updateSplitters();
37195     },
37196
37197     generateRules : function(cm){
37198         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37199         Roo.util.CSS.removeStyleSheet(rulesId);
37200         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37201             var cid = cm.getColumnId(i);
37202             var align = '';
37203             if(cm.config[i].align){
37204                 align = 'text-align:'+cm.config[i].align+';';
37205             }
37206             var hidden = '';
37207             if(cm.isHidden(i)){
37208                 hidden = 'display:none;';
37209             }
37210             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37211             ruleBuf.push(
37212                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37213                     this.hdSelector, cid, " {\n", align, width, "}\n",
37214                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37215                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37216         }
37217         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37218     },
37219
37220     updateSplitters : function(){
37221         var cm = this.cm, s = this.getSplitters();
37222         if(s){ // splitters not created yet
37223             var pos = 0, locked = true;
37224             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37225                 if(cm.isHidden(i)) continue;
37226                 var w = cm.getColumnWidth(i); // make sure it's a number
37227                 if(!cm.isLocked(i) && locked){
37228                     pos = 0;
37229                     locked = false;
37230                 }
37231                 pos += w;
37232                 s[i].style.left = (pos-this.splitOffset) + "px";
37233             }
37234         }
37235     },
37236
37237     handleHiddenChange : function(colModel, colIndex, hidden){
37238         if(hidden){
37239             this.hideColumn(colIndex);
37240         }else{
37241             this.unhideColumn(colIndex);
37242         }
37243     },
37244
37245     hideColumn : function(colIndex){
37246         var cid = this.getColumnId(colIndex);
37247         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37248         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37249         if(Roo.isSafari){
37250             this.updateHeaders();
37251         }
37252         this.updateSplitters();
37253         this.layout();
37254     },
37255
37256     unhideColumn : function(colIndex){
37257         var cid = this.getColumnId(colIndex);
37258         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37259         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37260
37261         if(Roo.isSafari){
37262             this.updateHeaders();
37263         }
37264         this.updateSplitters();
37265         this.layout();
37266     },
37267
37268     insertRows : function(dm, firstRow, lastRow, isUpdate){
37269         if(firstRow == 0 && lastRow == dm.getCount()-1){
37270             this.refresh();
37271         }else{
37272             if(!isUpdate){
37273                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37274             }
37275             var s = this.getScrollState();
37276             var markup = this.renderRows(firstRow, lastRow);
37277             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37278             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37279             this.restoreScroll(s);
37280             if(!isUpdate){
37281                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37282                 this.syncRowHeights(firstRow, lastRow);
37283                 this.stripeRows(firstRow);
37284                 this.layout();
37285             }
37286         }
37287     },
37288
37289     bufferRows : function(markup, target, index){
37290         var before = null, trows = target.rows, tbody = target.tBodies[0];
37291         if(index < trows.length){
37292             before = trows[index];
37293         }
37294         var b = document.createElement("div");
37295         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37296         var rows = b.firstChild.rows;
37297         for(var i = 0, len = rows.length; i < len; i++){
37298             if(before){
37299                 tbody.insertBefore(rows[0], before);
37300             }else{
37301                 tbody.appendChild(rows[0]);
37302             }
37303         }
37304         b.innerHTML = "";
37305         b = null;
37306     },
37307
37308     deleteRows : function(dm, firstRow, lastRow){
37309         if(dm.getRowCount()<1){
37310             this.fireEvent("beforerefresh", this);
37311             this.mainBody.update("");
37312             this.lockedBody.update("");
37313             this.fireEvent("refresh", this);
37314         }else{
37315             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37316             var bt = this.getBodyTable();
37317             var tbody = bt.firstChild;
37318             var rows = bt.rows;
37319             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37320                 tbody.removeChild(rows[firstRow]);
37321             }
37322             this.stripeRows(firstRow);
37323             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37324         }
37325     },
37326
37327     updateRows : function(dataSource, firstRow, lastRow){
37328         var s = this.getScrollState();
37329         this.refresh();
37330         this.restoreScroll(s);
37331     },
37332
37333     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37334         if(!noRefresh){
37335            this.refresh();
37336         }
37337         this.updateHeaderSortState();
37338     },
37339
37340     getScrollState : function(){
37341         
37342         var sb = this.scroller.dom;
37343         return {left: sb.scrollLeft, top: sb.scrollTop};
37344     },
37345
37346     stripeRows : function(startRow){
37347         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37348             return;
37349         }
37350         startRow = startRow || 0;
37351         var rows = this.getBodyTable().rows;
37352         var lrows = this.getLockedTable().rows;
37353         var cls = ' x-grid-row-alt ';
37354         for(var i = startRow, len = rows.length; i < len; i++){
37355             var row = rows[i], lrow = lrows[i];
37356             var isAlt = ((i+1) % 2 == 0);
37357             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37358             if(isAlt == hasAlt){
37359                 continue;
37360             }
37361             if(isAlt){
37362                 row.className += " x-grid-row-alt";
37363             }else{
37364                 row.className = row.className.replace("x-grid-row-alt", "");
37365             }
37366             if(lrow){
37367                 lrow.className = row.className;
37368             }
37369         }
37370     },
37371
37372     restoreScroll : function(state){
37373         //Roo.log('GridView.restoreScroll');
37374         var sb = this.scroller.dom;
37375         sb.scrollLeft = state.left;
37376         sb.scrollTop = state.top;
37377         this.syncScroll();
37378     },
37379
37380     syncScroll : function(){
37381         //Roo.log('GridView.syncScroll');
37382         var sb = this.scroller.dom;
37383         var sh = this.mainHd.dom;
37384         var bs = this.mainBody.dom;
37385         var lv = this.lockedBody.dom;
37386         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37387         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37388     },
37389
37390     handleScroll : function(e){
37391         this.syncScroll();
37392         var sb = this.scroller.dom;
37393         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37394         e.stopEvent();
37395     },
37396
37397     handleWheel : function(e){
37398         var d = e.getWheelDelta();
37399         this.scroller.dom.scrollTop -= d*22;
37400         // set this here to prevent jumpy scrolling on large tables
37401         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37402         e.stopEvent();
37403     },
37404
37405     renderRows : function(startRow, endRow){
37406         // pull in all the crap needed to render rows
37407         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37408         var colCount = cm.getColumnCount();
37409
37410         if(ds.getCount() < 1){
37411             return ["", ""];
37412         }
37413
37414         // build a map for all the columns
37415         var cs = [];
37416         for(var i = 0; i < colCount; i++){
37417             var name = cm.getDataIndex(i);
37418             cs[i] = {
37419                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37420                 renderer : cm.getRenderer(i),
37421                 id : cm.getColumnId(i),
37422                 locked : cm.isLocked(i)
37423             };
37424         }
37425
37426         startRow = startRow || 0;
37427         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37428
37429         // records to render
37430         var rs = ds.getRange(startRow, endRow);
37431
37432         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37433     },
37434
37435     // As much as I hate to duplicate code, this was branched because FireFox really hates
37436     // [].join("") on strings. The performance difference was substantial enough to
37437     // branch this function
37438     doRender : Roo.isGecko ?
37439             function(cs, rs, ds, startRow, colCount, stripe){
37440                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37441                 // buffers
37442                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37443                 
37444                 var hasListener = this.grid.hasListener('rowclass');
37445                 var rowcfg = {};
37446                 for(var j = 0, len = rs.length; j < len; j++){
37447                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37448                     for(var i = 0; i < colCount; i++){
37449                         c = cs[i];
37450                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37451                         p.id = c.id;
37452                         p.css = p.attr = "";
37453                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37454                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37455                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37456                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37457                         }
37458                         var markup = ct.apply(p);
37459                         if(!c.locked){
37460                             cb+= markup;
37461                         }else{
37462                             lcb+= markup;
37463                         }
37464                     }
37465                     var alt = [];
37466                     if(stripe && ((rowIndex+1) % 2 == 0)){
37467                         alt.push("x-grid-row-alt")
37468                     }
37469                     if(r.dirty){
37470                         alt.push(  " x-grid-dirty-row");
37471                     }
37472                     rp.cells = lcb;
37473                     if(this.getRowClass){
37474                         alt.push(this.getRowClass(r, rowIndex));
37475                     }
37476                     if (hasListener) {
37477                         rowcfg = {
37478                              
37479                             record: r,
37480                             rowIndex : rowIndex,
37481                             rowClass : ''
37482                         }
37483                         this.grid.fireEvent('rowclass', this, rowcfg);
37484                         alt.push(rowcfg.rowClass);
37485                     }
37486                     rp.alt = alt.join(" ");
37487                     lbuf+= rt.apply(rp);
37488                     rp.cells = cb;
37489                     buf+=  rt.apply(rp);
37490                 }
37491                 return [lbuf, buf];
37492             } :
37493             function(cs, rs, ds, startRow, colCount, stripe){
37494                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37495                 // buffers
37496                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37497                 var hasListener = this.grid.hasListener('rowclass');
37498  
37499                 var rowcfg = {};
37500                 for(var j = 0, len = rs.length; j < len; j++){
37501                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37502                     for(var i = 0; i < colCount; i++){
37503                         c = cs[i];
37504                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37505                         p.id = c.id;
37506                         p.css = p.attr = "";
37507                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37508                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37509                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37510                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37511                         }
37512                         
37513                         var markup = ct.apply(p);
37514                         if(!c.locked){
37515                             cb[cb.length] = markup;
37516                         }else{
37517                             lcb[lcb.length] = markup;
37518                         }
37519                     }
37520                     var alt = [];
37521                     if(stripe && ((rowIndex+1) % 2 == 0)){
37522                         alt.push( "x-grid-row-alt");
37523                     }
37524                     if(r.dirty){
37525                         alt.push(" x-grid-dirty-row");
37526                     }
37527                     rp.cells = lcb;
37528                     if(this.getRowClass){
37529                         alt.push( this.getRowClass(r, rowIndex));
37530                     }
37531                     if (hasListener) {
37532                         rowcfg = {
37533                              
37534                             record: r,
37535                             rowIndex : rowIndex,
37536                             rowClass : ''
37537                         }
37538                         this.grid.fireEvent('rowclass', this, rowcfg);
37539                         alt.push(rowcfg.rowClass);
37540                     }
37541                     rp.alt = alt.join(" ");
37542                     rp.cells = lcb.join("");
37543                     lbuf[lbuf.length] = rt.apply(rp);
37544                     rp.cells = cb.join("");
37545                     buf[buf.length] =  rt.apply(rp);
37546                 }
37547                 return [lbuf.join(""), buf.join("")];
37548             },
37549
37550     renderBody : function(){
37551         var markup = this.renderRows();
37552         var bt = this.templates.body;
37553         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37554     },
37555
37556     /**
37557      * Refreshes the grid
37558      * @param {Boolean} headersToo
37559      */
37560     refresh : function(headersToo){
37561         this.fireEvent("beforerefresh", this);
37562         this.grid.stopEditing();
37563         var result = this.renderBody();
37564         this.lockedBody.update(result[0]);
37565         this.mainBody.update(result[1]);
37566         if(headersToo === true){
37567             this.updateHeaders();
37568             this.updateColumns();
37569             this.updateSplitters();
37570             this.updateHeaderSortState();
37571         }
37572         this.syncRowHeights();
37573         this.layout();
37574         this.fireEvent("refresh", this);
37575     },
37576
37577     handleColumnMove : function(cm, oldIndex, newIndex){
37578         this.indexMap = null;
37579         var s = this.getScrollState();
37580         this.refresh(true);
37581         this.restoreScroll(s);
37582         this.afterMove(newIndex);
37583     },
37584
37585     afterMove : function(colIndex){
37586         if(this.enableMoveAnim && Roo.enableFx){
37587             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37588         }
37589         // if multisort - fix sortOrder, and reload..
37590         if (this.grid.dataSource.multiSort) {
37591             // the we can call sort again..
37592             var dm = this.grid.dataSource;
37593             var cm = this.grid.colModel;
37594             var so = [];
37595             for(var i = 0; i < cm.config.length; i++ ) {
37596                 
37597                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37598                     continue; // dont' bother, it's not in sort list or being set.
37599                 }
37600                 
37601                 so.push(cm.config[i].dataIndex);
37602             };
37603             dm.sortOrder = so;
37604             dm.load(dm.lastOptions);
37605             
37606             
37607         }
37608         
37609     },
37610
37611     updateCell : function(dm, rowIndex, dataIndex){
37612         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37613         if(typeof colIndex == "undefined"){ // not present in grid
37614             return;
37615         }
37616         var cm = this.grid.colModel;
37617         var cell = this.getCell(rowIndex, colIndex);
37618         var cellText = this.getCellText(rowIndex, colIndex);
37619
37620         var p = {
37621             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37622             id : cm.getColumnId(colIndex),
37623             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37624         };
37625         var renderer = cm.getRenderer(colIndex);
37626         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37627         if(typeof val == "undefined" || val === "") val = "&#160;";
37628         cellText.innerHTML = val;
37629         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37630         this.syncRowHeights(rowIndex, rowIndex);
37631     },
37632
37633     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37634         var maxWidth = 0;
37635         if(this.grid.autoSizeHeaders){
37636             var h = this.getHeaderCellMeasure(colIndex);
37637             maxWidth = Math.max(maxWidth, h.scrollWidth);
37638         }
37639         var tb, index;
37640         if(this.cm.isLocked(colIndex)){
37641             tb = this.getLockedTable();
37642             index = colIndex;
37643         }else{
37644             tb = this.getBodyTable();
37645             index = colIndex - this.cm.getLockedCount();
37646         }
37647         if(tb && tb.rows){
37648             var rows = tb.rows;
37649             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37650             for(var i = 0; i < stopIndex; i++){
37651                 var cell = rows[i].childNodes[index].firstChild;
37652                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37653             }
37654         }
37655         return maxWidth + /*margin for error in IE*/ 5;
37656     },
37657     /**
37658      * Autofit a column to its content.
37659      * @param {Number} colIndex
37660      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37661      */
37662      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37663          if(this.cm.isHidden(colIndex)){
37664              return; // can't calc a hidden column
37665          }
37666         if(forceMinSize){
37667             var cid = this.cm.getColumnId(colIndex);
37668             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37669            if(this.grid.autoSizeHeaders){
37670                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37671            }
37672         }
37673         var newWidth = this.calcColumnWidth(colIndex);
37674         this.cm.setColumnWidth(colIndex,
37675             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37676         if(!suppressEvent){
37677             this.grid.fireEvent("columnresize", colIndex, newWidth);
37678         }
37679     },
37680
37681     /**
37682      * Autofits all columns to their content and then expands to fit any extra space in the grid
37683      */
37684      autoSizeColumns : function(){
37685         var cm = this.grid.colModel;
37686         var colCount = cm.getColumnCount();
37687         for(var i = 0; i < colCount; i++){
37688             this.autoSizeColumn(i, true, true);
37689         }
37690         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37691             this.fitColumns();
37692         }else{
37693             this.updateColumns();
37694             this.layout();
37695         }
37696     },
37697
37698     /**
37699      * Autofits all columns to the grid's width proportionate with their current size
37700      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37701      */
37702     fitColumns : function(reserveScrollSpace){
37703         var cm = this.grid.colModel;
37704         var colCount = cm.getColumnCount();
37705         var cols = [];
37706         var width = 0;
37707         var i, w;
37708         for (i = 0; i < colCount; i++){
37709             if(!cm.isHidden(i) && !cm.isFixed(i)){
37710                 w = cm.getColumnWidth(i);
37711                 cols.push(i);
37712                 cols.push(w);
37713                 width += w;
37714             }
37715         }
37716         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37717         if(reserveScrollSpace){
37718             avail -= 17;
37719         }
37720         var frac = (avail - cm.getTotalWidth())/width;
37721         while (cols.length){
37722             w = cols.pop();
37723             i = cols.pop();
37724             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37725         }
37726         this.updateColumns();
37727         this.layout();
37728     },
37729
37730     onRowSelect : function(rowIndex){
37731         var row = this.getRowComposite(rowIndex);
37732         row.addClass("x-grid-row-selected");
37733     },
37734
37735     onRowDeselect : function(rowIndex){
37736         var row = this.getRowComposite(rowIndex);
37737         row.removeClass("x-grid-row-selected");
37738     },
37739
37740     onCellSelect : function(row, col){
37741         var cell = this.getCell(row, col);
37742         if(cell){
37743             Roo.fly(cell).addClass("x-grid-cell-selected");
37744         }
37745     },
37746
37747     onCellDeselect : function(row, col){
37748         var cell = this.getCell(row, col);
37749         if(cell){
37750             Roo.fly(cell).removeClass("x-grid-cell-selected");
37751         }
37752     },
37753
37754     updateHeaderSortState : function(){
37755         
37756         // sort state can be single { field: xxx, direction : yyy}
37757         // or   { xxx=>ASC , yyy : DESC ..... }
37758         
37759         var mstate = {};
37760         if (!this.ds.multiSort) { 
37761             var state = this.ds.getSortState();
37762             if(!state){
37763                 return;
37764             }
37765             mstate[state.field] = state.direction;
37766             // FIXME... - this is not used here.. but might be elsewhere..
37767             this.sortState = state;
37768             
37769         } else {
37770             mstate = this.ds.sortToggle;
37771         }
37772         //remove existing sort classes..
37773         
37774         var sc = this.sortClasses;
37775         var hds = this.el.select(this.headerSelector).removeClass(sc);
37776         
37777         for(var f in mstate) {
37778         
37779             var sortColumn = this.cm.findColumnIndex(f);
37780             
37781             if(sortColumn != -1){
37782                 var sortDir = mstate[f];        
37783                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37784             }
37785         }
37786         
37787          
37788         
37789     },
37790
37791
37792     handleHeaderClick : function(g, index,e){
37793         
37794         Roo.log("header click");
37795         
37796         if (Roo.isTouch) {
37797             // touch events on header are handled by context
37798             this.handleHdCtx(g,index,e);
37799             return;
37800         }
37801         
37802         
37803         if(this.headersDisabled){
37804             return;
37805         }
37806         var dm = g.dataSource, cm = g.colModel;
37807         if(!cm.isSortable(index)){
37808             return;
37809         }
37810         g.stopEditing();
37811         
37812         if (dm.multiSort) {
37813             // update the sortOrder
37814             var so = [];
37815             for(var i = 0; i < cm.config.length; i++ ) {
37816                 
37817                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37818                     continue; // dont' bother, it's not in sort list or being set.
37819                 }
37820                 
37821                 so.push(cm.config[i].dataIndex);
37822             };
37823             dm.sortOrder = so;
37824         }
37825         
37826         
37827         dm.sort(cm.getDataIndex(index));
37828     },
37829
37830
37831     destroy : function(){
37832         if(this.colMenu){
37833             this.colMenu.removeAll();
37834             Roo.menu.MenuMgr.unregister(this.colMenu);
37835             this.colMenu.getEl().remove();
37836             delete this.colMenu;
37837         }
37838         if(this.hmenu){
37839             this.hmenu.removeAll();
37840             Roo.menu.MenuMgr.unregister(this.hmenu);
37841             this.hmenu.getEl().remove();
37842             delete this.hmenu;
37843         }
37844         if(this.grid.enableColumnMove){
37845             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37846             if(dds){
37847                 for(var dd in dds){
37848                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37849                         var elid = dds[dd].dragElId;
37850                         dds[dd].unreg();
37851                         Roo.get(elid).remove();
37852                     } else if(dds[dd].config.isTarget){
37853                         dds[dd].proxyTop.remove();
37854                         dds[dd].proxyBottom.remove();
37855                         dds[dd].unreg();
37856                     }
37857                     if(Roo.dd.DDM.locationCache[dd]){
37858                         delete Roo.dd.DDM.locationCache[dd];
37859                     }
37860                 }
37861                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37862             }
37863         }
37864         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37865         this.bind(null, null);
37866         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37867     },
37868
37869     handleLockChange : function(){
37870         this.refresh(true);
37871     },
37872
37873     onDenyColumnLock : function(){
37874
37875     },
37876
37877     onDenyColumnHide : function(){
37878
37879     },
37880
37881     handleHdMenuClick : function(item){
37882         var index = this.hdCtxIndex;
37883         var cm = this.cm, ds = this.ds;
37884         switch(item.id){
37885             case "asc":
37886                 ds.sort(cm.getDataIndex(index), "ASC");
37887                 break;
37888             case "desc":
37889                 ds.sort(cm.getDataIndex(index), "DESC");
37890                 break;
37891             case "lock":
37892                 var lc = cm.getLockedCount();
37893                 if(cm.getColumnCount(true) <= lc+1){
37894                     this.onDenyColumnLock();
37895                     return;
37896                 }
37897                 if(lc != index){
37898                     cm.setLocked(index, true, true);
37899                     cm.moveColumn(index, lc);
37900                     this.grid.fireEvent("columnmove", index, lc);
37901                 }else{
37902                     cm.setLocked(index, true);
37903                 }
37904             break;
37905             case "unlock":
37906                 var lc = cm.getLockedCount();
37907                 if((lc-1) != index){
37908                     cm.setLocked(index, false, true);
37909                     cm.moveColumn(index, lc-1);
37910                     this.grid.fireEvent("columnmove", index, lc-1);
37911                 }else{
37912                     cm.setLocked(index, false);
37913                 }
37914             break;
37915             case 'wider': // used to expand cols on touch..
37916             case 'narrow':
37917                 var cw = cm.getColumnWidth(index);
37918                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37919                 cw = Math.max(0, cw);
37920                 cw = Math.min(cw,4000);
37921                 cm.setColumnWidth(index, cw);
37922                 break;
37923                 
37924             default:
37925                 index = cm.getIndexById(item.id.substr(4));
37926                 if(index != -1){
37927                     if(item.checked && cm.getColumnCount(true) <= 1){
37928                         this.onDenyColumnHide();
37929                         return false;
37930                     }
37931                     cm.setHidden(index, item.checked);
37932                 }
37933         }
37934         return true;
37935     },
37936
37937     beforeColMenuShow : function(){
37938         var cm = this.cm,  colCount = cm.getColumnCount();
37939         this.colMenu.removeAll();
37940         for(var i = 0; i < colCount; i++){
37941             this.colMenu.add(new Roo.menu.CheckItem({
37942                 id: "col-"+cm.getColumnId(i),
37943                 text: cm.getColumnHeader(i),
37944                 checked: !cm.isHidden(i),
37945                 hideOnClick:false
37946             }));
37947         }
37948     },
37949
37950     handleHdCtx : function(g, index, e){
37951         e.stopEvent();
37952         var hd = this.getHeaderCell(index);
37953         this.hdCtxIndex = index;
37954         var ms = this.hmenu.items, cm = this.cm;
37955         ms.get("asc").setDisabled(!cm.isSortable(index));
37956         ms.get("desc").setDisabled(!cm.isSortable(index));
37957         if(this.grid.enableColLock !== false){
37958             ms.get("lock").setDisabled(cm.isLocked(index));
37959             ms.get("unlock").setDisabled(!cm.isLocked(index));
37960         }
37961         this.hmenu.show(hd, "tl-bl");
37962     },
37963
37964     handleHdOver : function(e){
37965         var hd = this.findHeaderCell(e.getTarget());
37966         if(hd && !this.headersDisabled){
37967             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37968                this.fly(hd).addClass("x-grid-hd-over");
37969             }
37970         }
37971     },
37972
37973     handleHdOut : function(e){
37974         var hd = this.findHeaderCell(e.getTarget());
37975         if(hd){
37976             this.fly(hd).removeClass("x-grid-hd-over");
37977         }
37978     },
37979
37980     handleSplitDblClick : function(e, t){
37981         var i = this.getCellIndex(t);
37982         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37983             this.autoSizeColumn(i, true);
37984             this.layout();
37985         }
37986     },
37987
37988     render : function(){
37989
37990         var cm = this.cm;
37991         var colCount = cm.getColumnCount();
37992
37993         if(this.grid.monitorWindowResize === true){
37994             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37995         }
37996         var header = this.renderHeaders();
37997         var body = this.templates.body.apply({rows:""});
37998         var html = this.templates.master.apply({
37999             lockedBody: body,
38000             body: body,
38001             lockedHeader: header[0],
38002             header: header[1]
38003         });
38004
38005         //this.updateColumns();
38006
38007         this.grid.getGridEl().dom.innerHTML = html;
38008
38009         this.initElements();
38010         
38011         // a kludge to fix the random scolling effect in webkit
38012         this.el.on("scroll", function() {
38013             this.el.dom.scrollTop=0; // hopefully not recursive..
38014         },this);
38015
38016         this.scroller.on("scroll", this.handleScroll, this);
38017         this.lockedBody.on("mousewheel", this.handleWheel, this);
38018         this.mainBody.on("mousewheel", this.handleWheel, this);
38019
38020         this.mainHd.on("mouseover", this.handleHdOver, this);
38021         this.mainHd.on("mouseout", this.handleHdOut, this);
38022         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38023                 {delegate: "."+this.splitClass});
38024
38025         this.lockedHd.on("mouseover", this.handleHdOver, this);
38026         this.lockedHd.on("mouseout", this.handleHdOut, this);
38027         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38028                 {delegate: "."+this.splitClass});
38029
38030         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38031             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38032         }
38033
38034         this.updateSplitters();
38035
38036         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38037             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38038             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38039         }
38040
38041         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38042             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38043             this.hmenu.add(
38044                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38045                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38046             );
38047             if(this.grid.enableColLock !== false){
38048                 this.hmenu.add('-',
38049                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38050                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38051                 );
38052             }
38053             if (Roo.isTouch) {
38054                  this.hmenu.add('-',
38055                     {id:"wider", text: this.columnsWiderText},
38056                     {id:"narrow", text: this.columnsNarrowText }
38057                 );
38058                 
38059                  
38060             }
38061             
38062             if(this.grid.enableColumnHide !== false){
38063
38064                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38065                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38066                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38067
38068                 this.hmenu.add('-',
38069                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38070                 );
38071             }
38072             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38073
38074             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38075         }
38076
38077         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38078             this.dd = new Roo.grid.GridDragZone(this.grid, {
38079                 ddGroup : this.grid.ddGroup || 'GridDD'
38080             });
38081             
38082         }
38083
38084         /*
38085         for(var i = 0; i < colCount; i++){
38086             if(cm.isHidden(i)){
38087                 this.hideColumn(i);
38088             }
38089             if(cm.config[i].align){
38090                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38091                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38092             }
38093         }*/
38094         
38095         this.updateHeaderSortState();
38096
38097         this.beforeInitialResize();
38098         this.layout(true);
38099
38100         // two part rendering gives faster view to the user
38101         this.renderPhase2.defer(1, this);
38102     },
38103
38104     renderPhase2 : function(){
38105         // render the rows now
38106         this.refresh();
38107         if(this.grid.autoSizeColumns){
38108             this.autoSizeColumns();
38109         }
38110     },
38111
38112     beforeInitialResize : function(){
38113
38114     },
38115
38116     onColumnSplitterMoved : function(i, w){
38117         this.userResized = true;
38118         var cm = this.grid.colModel;
38119         cm.setColumnWidth(i, w, true);
38120         var cid = cm.getColumnId(i);
38121         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38122         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38123         this.updateSplitters();
38124         this.layout();
38125         this.grid.fireEvent("columnresize", i, w);
38126     },
38127
38128     syncRowHeights : function(startIndex, endIndex){
38129         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38130             startIndex = startIndex || 0;
38131             var mrows = this.getBodyTable().rows;
38132             var lrows = this.getLockedTable().rows;
38133             var len = mrows.length-1;
38134             endIndex = Math.min(endIndex || len, len);
38135             for(var i = startIndex; i <= endIndex; i++){
38136                 var m = mrows[i], l = lrows[i];
38137                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38138                 m.style.height = l.style.height = h + "px";
38139             }
38140         }
38141     },
38142
38143     layout : function(initialRender, is2ndPass){
38144         var g = this.grid;
38145         var auto = g.autoHeight;
38146         var scrollOffset = 16;
38147         var c = g.getGridEl(), cm = this.cm,
38148                 expandCol = g.autoExpandColumn,
38149                 gv = this;
38150         //c.beginMeasure();
38151
38152         if(!c.dom.offsetWidth){ // display:none?
38153             if(initialRender){
38154                 this.lockedWrap.show();
38155                 this.mainWrap.show();
38156             }
38157             return;
38158         }
38159
38160         var hasLock = this.cm.isLocked(0);
38161
38162         var tbh = this.headerPanel.getHeight();
38163         var bbh = this.footerPanel.getHeight();
38164
38165         if(auto){
38166             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38167             var newHeight = ch + c.getBorderWidth("tb");
38168             if(g.maxHeight){
38169                 newHeight = Math.min(g.maxHeight, newHeight);
38170             }
38171             c.setHeight(newHeight);
38172         }
38173
38174         if(g.autoWidth){
38175             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38176         }
38177
38178         var s = this.scroller;
38179
38180         var csize = c.getSize(true);
38181
38182         this.el.setSize(csize.width, csize.height);
38183
38184         this.headerPanel.setWidth(csize.width);
38185         this.footerPanel.setWidth(csize.width);
38186
38187         var hdHeight = this.mainHd.getHeight();
38188         var vw = csize.width;
38189         var vh = csize.height - (tbh + bbh);
38190
38191         s.setSize(vw, vh);
38192
38193         var bt = this.getBodyTable();
38194         var ltWidth = hasLock ?
38195                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38196
38197         var scrollHeight = bt.offsetHeight;
38198         var scrollWidth = ltWidth + bt.offsetWidth;
38199         var vscroll = false, hscroll = false;
38200
38201         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38202
38203         var lw = this.lockedWrap, mw = this.mainWrap;
38204         var lb = this.lockedBody, mb = this.mainBody;
38205
38206         setTimeout(function(){
38207             var t = s.dom.offsetTop;
38208             var w = s.dom.clientWidth,
38209                 h = s.dom.clientHeight;
38210
38211             lw.setTop(t);
38212             lw.setSize(ltWidth, h);
38213
38214             mw.setLeftTop(ltWidth, t);
38215             mw.setSize(w-ltWidth, h);
38216
38217             lb.setHeight(h-hdHeight);
38218             mb.setHeight(h-hdHeight);
38219
38220             if(is2ndPass !== true && !gv.userResized && expandCol){
38221                 // high speed resize without full column calculation
38222                 
38223                 var ci = cm.getIndexById(expandCol);
38224                 if (ci < 0) {
38225                     ci = cm.findColumnIndex(expandCol);
38226                 }
38227                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38228                 var expandId = cm.getColumnId(ci);
38229                 var  tw = cm.getTotalWidth(false);
38230                 var currentWidth = cm.getColumnWidth(ci);
38231                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38232                 if(currentWidth != cw){
38233                     cm.setColumnWidth(ci, cw, true);
38234                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38235                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38236                     gv.updateSplitters();
38237                     gv.layout(false, true);
38238                 }
38239             }
38240
38241             if(initialRender){
38242                 lw.show();
38243                 mw.show();
38244             }
38245             //c.endMeasure();
38246         }, 10);
38247     },
38248
38249     onWindowResize : function(){
38250         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38251             return;
38252         }
38253         this.layout();
38254     },
38255
38256     appendFooter : function(parentEl){
38257         return null;
38258     },
38259
38260     sortAscText : "Sort Ascending",
38261     sortDescText : "Sort Descending",
38262     lockText : "Lock Column",
38263     unlockText : "Unlock Column",
38264     columnsText : "Columns",
38265  
38266     columnsWiderText : "Wider",
38267     columnsNarrowText : "Thinner"
38268 });
38269
38270
38271 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38272     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38273     this.proxy.el.addClass('x-grid3-col-dd');
38274 };
38275
38276 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38277     handleMouseDown : function(e){
38278
38279     },
38280
38281     callHandleMouseDown : function(e){
38282         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38283     }
38284 });
38285 /*
38286  * Based on:
38287  * Ext JS Library 1.1.1
38288  * Copyright(c) 2006-2007, Ext JS, LLC.
38289  *
38290  * Originally Released Under LGPL - original licence link has changed is not relivant.
38291  *
38292  * Fork - LGPL
38293  * <script type="text/javascript">
38294  */
38295  
38296 // private
38297 // This is a support class used internally by the Grid components
38298 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38299     this.grid = grid;
38300     this.view = grid.getView();
38301     this.proxy = this.view.resizeProxy;
38302     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38303         "gridSplitters" + this.grid.getGridEl().id, {
38304         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38305     });
38306     this.setHandleElId(Roo.id(hd));
38307     this.setOuterHandleElId(Roo.id(hd2));
38308     this.scroll = false;
38309 };
38310 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38311     fly: Roo.Element.fly,
38312
38313     b4StartDrag : function(x, y){
38314         this.view.headersDisabled = true;
38315         this.proxy.setHeight(this.view.mainWrap.getHeight());
38316         var w = this.cm.getColumnWidth(this.cellIndex);
38317         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38318         this.resetConstraints();
38319         this.setXConstraint(minw, 1000);
38320         this.setYConstraint(0, 0);
38321         this.minX = x - minw;
38322         this.maxX = x + 1000;
38323         this.startPos = x;
38324         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38325     },
38326
38327
38328     handleMouseDown : function(e){
38329         ev = Roo.EventObject.setEvent(e);
38330         var t = this.fly(ev.getTarget());
38331         if(t.hasClass("x-grid-split")){
38332             this.cellIndex = this.view.getCellIndex(t.dom);
38333             this.split = t.dom;
38334             this.cm = this.grid.colModel;
38335             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38336                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38337             }
38338         }
38339     },
38340
38341     endDrag : function(e){
38342         this.view.headersDisabled = false;
38343         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38344         var diff = endX - this.startPos;
38345         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38346     },
38347
38348     autoOffset : function(){
38349         this.setDelta(0,0);
38350     }
38351 });/*
38352  * Based on:
38353  * Ext JS Library 1.1.1
38354  * Copyright(c) 2006-2007, Ext JS, LLC.
38355  *
38356  * Originally Released Under LGPL - original licence link has changed is not relivant.
38357  *
38358  * Fork - LGPL
38359  * <script type="text/javascript">
38360  */
38361  
38362 // private
38363 // This is a support class used internally by the Grid components
38364 Roo.grid.GridDragZone = function(grid, config){
38365     this.view = grid.getView();
38366     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38367     if(this.view.lockedBody){
38368         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38369         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38370     }
38371     this.scroll = false;
38372     this.grid = grid;
38373     this.ddel = document.createElement('div');
38374     this.ddel.className = 'x-grid-dd-wrap';
38375 };
38376
38377 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38378     ddGroup : "GridDD",
38379
38380     getDragData : function(e){
38381         var t = Roo.lib.Event.getTarget(e);
38382         var rowIndex = this.view.findRowIndex(t);
38383         var sm = this.grid.selModel;
38384             
38385         //Roo.log(rowIndex);
38386         
38387         if (sm.getSelectedCell) {
38388             // cell selection..
38389             if (!sm.getSelectedCell()) {
38390                 return false;
38391             }
38392             if (rowIndex != sm.getSelectedCell()[0]) {
38393                 return false;
38394             }
38395         
38396         }
38397         
38398         if(rowIndex !== false){
38399             
38400             // if editorgrid.. 
38401             
38402             
38403             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38404                
38405             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38406               //  
38407             //}
38408             if (e.hasModifier()){
38409                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38410             }
38411             
38412             Roo.log("getDragData");
38413             
38414             return {
38415                 grid: this.grid,
38416                 ddel: this.ddel,
38417                 rowIndex: rowIndex,
38418                 selections:sm.getSelections ? sm.getSelections() : (
38419                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38420                 )
38421             };
38422         }
38423         return false;
38424     },
38425
38426     onInitDrag : function(e){
38427         var data = this.dragData;
38428         this.ddel.innerHTML = this.grid.getDragDropText();
38429         this.proxy.update(this.ddel);
38430         // fire start drag?
38431     },
38432
38433     afterRepair : function(){
38434         this.dragging = false;
38435     },
38436
38437     getRepairXY : function(e, data){
38438         return false;
38439     },
38440
38441     onEndDrag : function(data, e){
38442         // fire end drag?
38443     },
38444
38445     onValidDrop : function(dd, e, id){
38446         // fire drag drop?
38447         this.hideProxy();
38448     },
38449
38450     beforeInvalidDrop : function(e, id){
38451
38452     }
38453 });/*
38454  * Based on:
38455  * Ext JS Library 1.1.1
38456  * Copyright(c) 2006-2007, Ext JS, LLC.
38457  *
38458  * Originally Released Under LGPL - original licence link has changed is not relivant.
38459  *
38460  * Fork - LGPL
38461  * <script type="text/javascript">
38462  */
38463  
38464
38465 /**
38466  * @class Roo.grid.ColumnModel
38467  * @extends Roo.util.Observable
38468  * This is the default implementation of a ColumnModel used by the Grid. It defines
38469  * the columns in the grid.
38470  * <br>Usage:<br>
38471  <pre><code>
38472  var colModel = new Roo.grid.ColumnModel([
38473         {header: "Ticker", width: 60, sortable: true, locked: true},
38474         {header: "Company Name", width: 150, sortable: true},
38475         {header: "Market Cap.", width: 100, sortable: true},
38476         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38477         {header: "Employees", width: 100, sortable: true, resizable: false}
38478  ]);
38479  </code></pre>
38480  * <p>
38481  
38482  * The config options listed for this class are options which may appear in each
38483  * individual column definition.
38484  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38485  * @constructor
38486  * @param {Object} config An Array of column config objects. See this class's
38487  * config objects for details.
38488 */
38489 Roo.grid.ColumnModel = function(config){
38490         /**
38491      * The config passed into the constructor
38492      */
38493     this.config = config;
38494     this.lookup = {};
38495
38496     // if no id, create one
38497     // if the column does not have a dataIndex mapping,
38498     // map it to the order it is in the config
38499     for(var i = 0, len = config.length; i < len; i++){
38500         var c = config[i];
38501         if(typeof c.dataIndex == "undefined"){
38502             c.dataIndex = i;
38503         }
38504         if(typeof c.renderer == "string"){
38505             c.renderer = Roo.util.Format[c.renderer];
38506         }
38507         if(typeof c.id == "undefined"){
38508             c.id = Roo.id();
38509         }
38510         if(c.editor && c.editor.xtype){
38511             c.editor  = Roo.factory(c.editor, Roo.grid);
38512         }
38513         if(c.editor && c.editor.isFormField){
38514             c.editor = new Roo.grid.GridEditor(c.editor);
38515         }
38516         this.lookup[c.id] = c;
38517     }
38518
38519     /**
38520      * The width of columns which have no width specified (defaults to 100)
38521      * @type Number
38522      */
38523     this.defaultWidth = 100;
38524
38525     /**
38526      * Default sortable of columns which have no sortable specified (defaults to false)
38527      * @type Boolean
38528      */
38529     this.defaultSortable = false;
38530
38531     this.addEvents({
38532         /**
38533              * @event widthchange
38534              * Fires when the width of a column changes.
38535              * @param {ColumnModel} this
38536              * @param {Number} columnIndex The column index
38537              * @param {Number} newWidth The new width
38538              */
38539             "widthchange": true,
38540         /**
38541              * @event headerchange
38542              * Fires when the text of a header changes.
38543              * @param {ColumnModel} this
38544              * @param {Number} columnIndex The column index
38545              * @param {Number} newText The new header text
38546              */
38547             "headerchange": true,
38548         /**
38549              * @event hiddenchange
38550              * Fires when a column is hidden or "unhidden".
38551              * @param {ColumnModel} this
38552              * @param {Number} columnIndex The column index
38553              * @param {Boolean} hidden true if hidden, false otherwise
38554              */
38555             "hiddenchange": true,
38556             /**
38557          * @event columnmoved
38558          * Fires when a column is moved.
38559          * @param {ColumnModel} this
38560          * @param {Number} oldIndex
38561          * @param {Number} newIndex
38562          */
38563         "columnmoved" : true,
38564         /**
38565          * @event columlockchange
38566          * Fires when a column's locked state is changed
38567          * @param {ColumnModel} this
38568          * @param {Number} colIndex
38569          * @param {Boolean} locked true if locked
38570          */
38571         "columnlockchange" : true
38572     });
38573     Roo.grid.ColumnModel.superclass.constructor.call(this);
38574 };
38575 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38576     /**
38577      * @cfg {String} header The header text to display in the Grid view.
38578      */
38579     /**
38580      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38581      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38582      * specified, the column's index is used as an index into the Record's data Array.
38583      */
38584     /**
38585      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38586      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38587      */
38588     /**
38589      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38590      * Defaults to the value of the {@link #defaultSortable} property.
38591      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38592      */
38593     /**
38594      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38595      */
38596     /**
38597      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38598      */
38599     /**
38600      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38601      */
38602     /**
38603      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38604      */
38605     /**
38606      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38607      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38608      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38609      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38610      */
38611        /**
38612      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38613      */
38614     /**
38615      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38616      */
38617     /**
38618      * @cfg {String} cursor (Optional)
38619      */
38620     /**
38621      * Returns the id of the column at the specified index.
38622      * @param {Number} index The column index
38623      * @return {String} the id
38624      */
38625     getColumnId : function(index){
38626         return this.config[index].id;
38627     },
38628
38629     /**
38630      * Returns the column for a specified id.
38631      * @param {String} id The column id
38632      * @return {Object} the column
38633      */
38634     getColumnById : function(id){
38635         return this.lookup[id];
38636     },
38637
38638     
38639     /**
38640      * Returns the column for a specified dataIndex.
38641      * @param {String} dataIndex The column dataIndex
38642      * @return {Object|Boolean} the column or false if not found
38643      */
38644     getColumnByDataIndex: function(dataIndex){
38645         var index = this.findColumnIndex(dataIndex);
38646         return index > -1 ? this.config[index] : false;
38647     },
38648     
38649     /**
38650      * Returns the index for a specified column id.
38651      * @param {String} id The column id
38652      * @return {Number} the index, or -1 if not found
38653      */
38654     getIndexById : function(id){
38655         for(var i = 0, len = this.config.length; i < len; i++){
38656             if(this.config[i].id == id){
38657                 return i;
38658             }
38659         }
38660         return -1;
38661     },
38662     
38663     /**
38664      * Returns the index for a specified column dataIndex.
38665      * @param {String} dataIndex The column dataIndex
38666      * @return {Number} the index, or -1 if not found
38667      */
38668     
38669     findColumnIndex : function(dataIndex){
38670         for(var i = 0, len = this.config.length; i < len; i++){
38671             if(this.config[i].dataIndex == dataIndex){
38672                 return i;
38673             }
38674         }
38675         return -1;
38676     },
38677     
38678     
38679     moveColumn : function(oldIndex, newIndex){
38680         var c = this.config[oldIndex];
38681         this.config.splice(oldIndex, 1);
38682         this.config.splice(newIndex, 0, c);
38683         this.dataMap = null;
38684         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38685     },
38686
38687     isLocked : function(colIndex){
38688         return this.config[colIndex].locked === true;
38689     },
38690
38691     setLocked : function(colIndex, value, suppressEvent){
38692         if(this.isLocked(colIndex) == value){
38693             return;
38694         }
38695         this.config[colIndex].locked = value;
38696         if(!suppressEvent){
38697             this.fireEvent("columnlockchange", this, colIndex, value);
38698         }
38699     },
38700
38701     getTotalLockedWidth : function(){
38702         var totalWidth = 0;
38703         for(var i = 0; i < this.config.length; i++){
38704             if(this.isLocked(i) && !this.isHidden(i)){
38705                 this.totalWidth += this.getColumnWidth(i);
38706             }
38707         }
38708         return totalWidth;
38709     },
38710
38711     getLockedCount : function(){
38712         for(var i = 0, len = this.config.length; i < len; i++){
38713             if(!this.isLocked(i)){
38714                 return i;
38715             }
38716         }
38717     },
38718
38719     /**
38720      * Returns the number of columns.
38721      * @return {Number}
38722      */
38723     getColumnCount : function(visibleOnly){
38724         if(visibleOnly === true){
38725             var c = 0;
38726             for(var i = 0, len = this.config.length; i < len; i++){
38727                 if(!this.isHidden(i)){
38728                     c++;
38729                 }
38730             }
38731             return c;
38732         }
38733         return this.config.length;
38734     },
38735
38736     /**
38737      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38738      * @param {Function} fn
38739      * @param {Object} scope (optional)
38740      * @return {Array} result
38741      */
38742     getColumnsBy : function(fn, scope){
38743         var r = [];
38744         for(var i = 0, len = this.config.length; i < len; i++){
38745             var c = this.config[i];
38746             if(fn.call(scope||this, c, i) === true){
38747                 r[r.length] = c;
38748             }
38749         }
38750         return r;
38751     },
38752
38753     /**
38754      * Returns true if the specified column is sortable.
38755      * @param {Number} col The column index
38756      * @return {Boolean}
38757      */
38758     isSortable : function(col){
38759         if(typeof this.config[col].sortable == "undefined"){
38760             return this.defaultSortable;
38761         }
38762         return this.config[col].sortable;
38763     },
38764
38765     /**
38766      * Returns the rendering (formatting) function defined for the column.
38767      * @param {Number} col The column index.
38768      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38769      */
38770     getRenderer : function(col){
38771         if(!this.config[col].renderer){
38772             return Roo.grid.ColumnModel.defaultRenderer;
38773         }
38774         return this.config[col].renderer;
38775     },
38776
38777     /**
38778      * Sets the rendering (formatting) function for a column.
38779      * @param {Number} col The column index
38780      * @param {Function} fn The function to use to process the cell's raw data
38781      * to return HTML markup for the grid view. The render function is called with
38782      * the following parameters:<ul>
38783      * <li>Data value.</li>
38784      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38785      * <li>css A CSS style string to apply to the table cell.</li>
38786      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38787      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38788      * <li>Row index</li>
38789      * <li>Column index</li>
38790      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38791      */
38792     setRenderer : function(col, fn){
38793         this.config[col].renderer = fn;
38794     },
38795
38796     /**
38797      * Returns the width for the specified column.
38798      * @param {Number} col The column index
38799      * @return {Number}
38800      */
38801     getColumnWidth : function(col){
38802         return this.config[col].width * 1 || this.defaultWidth;
38803     },
38804
38805     /**
38806      * Sets the width for a column.
38807      * @param {Number} col The column index
38808      * @param {Number} width The new width
38809      */
38810     setColumnWidth : function(col, width, suppressEvent){
38811         this.config[col].width = width;
38812         this.totalWidth = null;
38813         if(!suppressEvent){
38814              this.fireEvent("widthchange", this, col, width);
38815         }
38816     },
38817
38818     /**
38819      * Returns the total width of all columns.
38820      * @param {Boolean} includeHidden True to include hidden column widths
38821      * @return {Number}
38822      */
38823     getTotalWidth : function(includeHidden){
38824         if(!this.totalWidth){
38825             this.totalWidth = 0;
38826             for(var i = 0, len = this.config.length; i < len; i++){
38827                 if(includeHidden || !this.isHidden(i)){
38828                     this.totalWidth += this.getColumnWidth(i);
38829                 }
38830             }
38831         }
38832         return this.totalWidth;
38833     },
38834
38835     /**
38836      * Returns the header for the specified column.
38837      * @param {Number} col The column index
38838      * @return {String}
38839      */
38840     getColumnHeader : function(col){
38841         return this.config[col].header;
38842     },
38843
38844     /**
38845      * Sets the header for a column.
38846      * @param {Number} col The column index
38847      * @param {String} header The new header
38848      */
38849     setColumnHeader : function(col, header){
38850         this.config[col].header = header;
38851         this.fireEvent("headerchange", this, col, header);
38852     },
38853
38854     /**
38855      * Returns the tooltip for the specified column.
38856      * @param {Number} col The column index
38857      * @return {String}
38858      */
38859     getColumnTooltip : function(col){
38860             return this.config[col].tooltip;
38861     },
38862     /**
38863      * Sets the tooltip for a column.
38864      * @param {Number} col The column index
38865      * @param {String} tooltip The new tooltip
38866      */
38867     setColumnTooltip : function(col, tooltip){
38868             this.config[col].tooltip = tooltip;
38869     },
38870
38871     /**
38872      * Returns the dataIndex for the specified column.
38873      * @param {Number} col The column index
38874      * @return {Number}
38875      */
38876     getDataIndex : function(col){
38877         return this.config[col].dataIndex;
38878     },
38879
38880     /**
38881      * Sets the dataIndex for a column.
38882      * @param {Number} col The column index
38883      * @param {Number} dataIndex The new dataIndex
38884      */
38885     setDataIndex : function(col, dataIndex){
38886         this.config[col].dataIndex = dataIndex;
38887     },
38888
38889     
38890     
38891     /**
38892      * Returns true if the cell is editable.
38893      * @param {Number} colIndex The column index
38894      * @param {Number} rowIndex The row index
38895      * @return {Boolean}
38896      */
38897     isCellEditable : function(colIndex, rowIndex){
38898         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38899     },
38900
38901     /**
38902      * Returns the editor defined for the cell/column.
38903      * return false or null to disable editing.
38904      * @param {Number} colIndex The column index
38905      * @param {Number} rowIndex The row index
38906      * @return {Object}
38907      */
38908     getCellEditor : function(colIndex, rowIndex){
38909         return this.config[colIndex].editor;
38910     },
38911
38912     /**
38913      * Sets if a column is editable.
38914      * @param {Number} col The column index
38915      * @param {Boolean} editable True if the column is editable
38916      */
38917     setEditable : function(col, editable){
38918         this.config[col].editable = editable;
38919     },
38920
38921
38922     /**
38923      * Returns true if the column is hidden.
38924      * @param {Number} colIndex The column index
38925      * @return {Boolean}
38926      */
38927     isHidden : function(colIndex){
38928         return this.config[colIndex].hidden;
38929     },
38930
38931
38932     /**
38933      * Returns true if the column width cannot be changed
38934      */
38935     isFixed : function(colIndex){
38936         return this.config[colIndex].fixed;
38937     },
38938
38939     /**
38940      * Returns true if the column can be resized
38941      * @return {Boolean}
38942      */
38943     isResizable : function(colIndex){
38944         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38945     },
38946     /**
38947      * Sets if a column is hidden.
38948      * @param {Number} colIndex The column index
38949      * @param {Boolean} hidden True if the column is hidden
38950      */
38951     setHidden : function(colIndex, hidden){
38952         this.config[colIndex].hidden = hidden;
38953         this.totalWidth = null;
38954         this.fireEvent("hiddenchange", this, colIndex, hidden);
38955     },
38956
38957     /**
38958      * Sets the editor for a column.
38959      * @param {Number} col The column index
38960      * @param {Object} editor The editor object
38961      */
38962     setEditor : function(col, editor){
38963         this.config[col].editor = editor;
38964     }
38965 });
38966
38967 Roo.grid.ColumnModel.defaultRenderer = function(value){
38968         if(typeof value == "string" && value.length < 1){
38969             return "&#160;";
38970         }
38971         return value;
38972 };
38973
38974 // Alias for backwards compatibility
38975 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38976 /*
38977  * Based on:
38978  * Ext JS Library 1.1.1
38979  * Copyright(c) 2006-2007, Ext JS, LLC.
38980  *
38981  * Originally Released Under LGPL - original licence link has changed is not relivant.
38982  *
38983  * Fork - LGPL
38984  * <script type="text/javascript">
38985  */
38986
38987 /**
38988  * @class Roo.grid.AbstractSelectionModel
38989  * @extends Roo.util.Observable
38990  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38991  * implemented by descendant classes.  This class should not be directly instantiated.
38992  * @constructor
38993  */
38994 Roo.grid.AbstractSelectionModel = function(){
38995     this.locked = false;
38996     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38997 };
38998
38999 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39000     /** @ignore Called by the grid automatically. Do not call directly. */
39001     init : function(grid){
39002         this.grid = grid;
39003         this.initEvents();
39004     },
39005
39006     /**
39007      * Locks the selections.
39008      */
39009     lock : function(){
39010         this.locked = true;
39011     },
39012
39013     /**
39014      * Unlocks the selections.
39015      */
39016     unlock : function(){
39017         this.locked = false;
39018     },
39019
39020     /**
39021      * Returns true if the selections are locked.
39022      * @return {Boolean}
39023      */
39024     isLocked : function(){
39025         return this.locked;
39026     }
39027 });/*
39028  * Based on:
39029  * Ext JS Library 1.1.1
39030  * Copyright(c) 2006-2007, Ext JS, LLC.
39031  *
39032  * Originally Released Under LGPL - original licence link has changed is not relivant.
39033  *
39034  * Fork - LGPL
39035  * <script type="text/javascript">
39036  */
39037 /**
39038  * @extends Roo.grid.AbstractSelectionModel
39039  * @class Roo.grid.RowSelectionModel
39040  * The default SelectionModel used by {@link Roo.grid.Grid}.
39041  * It supports multiple selections and keyboard selection/navigation. 
39042  * @constructor
39043  * @param {Object} config
39044  */
39045 Roo.grid.RowSelectionModel = function(config){
39046     Roo.apply(this, config);
39047     this.selections = new Roo.util.MixedCollection(false, function(o){
39048         return o.id;
39049     });
39050
39051     this.last = false;
39052     this.lastActive = false;
39053
39054     this.addEvents({
39055         /**
39056              * @event selectionchange
39057              * Fires when the selection changes
39058              * @param {SelectionModel} this
39059              */
39060             "selectionchange" : true,
39061         /**
39062              * @event afterselectionchange
39063              * Fires after the selection changes (eg. by key press or clicking)
39064              * @param {SelectionModel} this
39065              */
39066             "afterselectionchange" : true,
39067         /**
39068              * @event beforerowselect
39069              * Fires when a row is selected being selected, return false to cancel.
39070              * @param {SelectionModel} this
39071              * @param {Number} rowIndex The selected index
39072              * @param {Boolean} keepExisting False if other selections will be cleared
39073              */
39074             "beforerowselect" : true,
39075         /**
39076              * @event rowselect
39077              * Fires when a row is selected.
39078              * @param {SelectionModel} this
39079              * @param {Number} rowIndex The selected index
39080              * @param {Roo.data.Record} r The record
39081              */
39082             "rowselect" : true,
39083         /**
39084              * @event rowdeselect
39085              * Fires when a row is deselected.
39086              * @param {SelectionModel} this
39087              * @param {Number} rowIndex The selected index
39088              */
39089         "rowdeselect" : true
39090     });
39091     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39092     this.locked = false;
39093 };
39094
39095 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39096     /**
39097      * @cfg {Boolean} singleSelect
39098      * True to allow selection of only one row at a time (defaults to false)
39099      */
39100     singleSelect : false,
39101
39102     // private
39103     initEvents : function(){
39104
39105         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39106             this.grid.on("mousedown", this.handleMouseDown, this);
39107         }else{ // allow click to work like normal
39108             this.grid.on("rowclick", this.handleDragableRowClick, this);
39109         }
39110
39111         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39112             "up" : function(e){
39113                 if(!e.shiftKey){
39114                     this.selectPrevious(e.shiftKey);
39115                 }else if(this.last !== false && this.lastActive !== false){
39116                     var last = this.last;
39117                     this.selectRange(this.last,  this.lastActive-1);
39118                     this.grid.getView().focusRow(this.lastActive);
39119                     if(last !== false){
39120                         this.last = last;
39121                     }
39122                 }else{
39123                     this.selectFirstRow();
39124                 }
39125                 this.fireEvent("afterselectionchange", this);
39126             },
39127             "down" : function(e){
39128                 if(!e.shiftKey){
39129                     this.selectNext(e.shiftKey);
39130                 }else if(this.last !== false && this.lastActive !== false){
39131                     var last = this.last;
39132                     this.selectRange(this.last,  this.lastActive+1);
39133                     this.grid.getView().focusRow(this.lastActive);
39134                     if(last !== false){
39135                         this.last = last;
39136                     }
39137                 }else{
39138                     this.selectFirstRow();
39139                 }
39140                 this.fireEvent("afterselectionchange", this);
39141             },
39142             scope: this
39143         });
39144
39145         var view = this.grid.view;
39146         view.on("refresh", this.onRefresh, this);
39147         view.on("rowupdated", this.onRowUpdated, this);
39148         view.on("rowremoved", this.onRemove, this);
39149     },
39150
39151     // private
39152     onRefresh : function(){
39153         var ds = this.grid.dataSource, i, v = this.grid.view;
39154         var s = this.selections;
39155         s.each(function(r){
39156             if((i = ds.indexOfId(r.id)) != -1){
39157                 v.onRowSelect(i);
39158             }else{
39159                 s.remove(r);
39160             }
39161         });
39162     },
39163
39164     // private
39165     onRemove : function(v, index, r){
39166         this.selections.remove(r);
39167     },
39168
39169     // private
39170     onRowUpdated : function(v, index, r){
39171         if(this.isSelected(r)){
39172             v.onRowSelect(index);
39173         }
39174     },
39175
39176     /**
39177      * Select records.
39178      * @param {Array} records The records to select
39179      * @param {Boolean} keepExisting (optional) True to keep existing selections
39180      */
39181     selectRecords : function(records, keepExisting){
39182         if(!keepExisting){
39183             this.clearSelections();
39184         }
39185         var ds = this.grid.dataSource;
39186         for(var i = 0, len = records.length; i < len; i++){
39187             this.selectRow(ds.indexOf(records[i]), true);
39188         }
39189     },
39190
39191     /**
39192      * Gets the number of selected rows.
39193      * @return {Number}
39194      */
39195     getCount : function(){
39196         return this.selections.length;
39197     },
39198
39199     /**
39200      * Selects the first row in the grid.
39201      */
39202     selectFirstRow : function(){
39203         this.selectRow(0);
39204     },
39205
39206     /**
39207      * Select the last row.
39208      * @param {Boolean} keepExisting (optional) True to keep existing selections
39209      */
39210     selectLastRow : function(keepExisting){
39211         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39212     },
39213
39214     /**
39215      * Selects the row immediately following the last selected row.
39216      * @param {Boolean} keepExisting (optional) True to keep existing selections
39217      */
39218     selectNext : function(keepExisting){
39219         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39220             this.selectRow(this.last+1, keepExisting);
39221             this.grid.getView().focusRow(this.last);
39222         }
39223     },
39224
39225     /**
39226      * Selects the row that precedes the last selected row.
39227      * @param {Boolean} keepExisting (optional) True to keep existing selections
39228      */
39229     selectPrevious : function(keepExisting){
39230         if(this.last){
39231             this.selectRow(this.last-1, keepExisting);
39232             this.grid.getView().focusRow(this.last);
39233         }
39234     },
39235
39236     /**
39237      * Returns the selected records
39238      * @return {Array} Array of selected records
39239      */
39240     getSelections : function(){
39241         return [].concat(this.selections.items);
39242     },
39243
39244     /**
39245      * Returns the first selected record.
39246      * @return {Record}
39247      */
39248     getSelected : function(){
39249         return this.selections.itemAt(0);
39250     },
39251
39252
39253     /**
39254      * Clears all selections.
39255      */
39256     clearSelections : function(fast){
39257         if(this.locked) return;
39258         if(fast !== true){
39259             var ds = this.grid.dataSource;
39260             var s = this.selections;
39261             s.each(function(r){
39262                 this.deselectRow(ds.indexOfId(r.id));
39263             }, this);
39264             s.clear();
39265         }else{
39266             this.selections.clear();
39267         }
39268         this.last = false;
39269     },
39270
39271
39272     /**
39273      * Selects all rows.
39274      */
39275     selectAll : function(){
39276         if(this.locked) return;
39277         this.selections.clear();
39278         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39279             this.selectRow(i, true);
39280         }
39281     },
39282
39283     /**
39284      * Returns True if there is a selection.
39285      * @return {Boolean}
39286      */
39287     hasSelection : function(){
39288         return this.selections.length > 0;
39289     },
39290
39291     /**
39292      * Returns True if the specified row is selected.
39293      * @param {Number/Record} record The record or index of the record to check
39294      * @return {Boolean}
39295      */
39296     isSelected : function(index){
39297         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39298         return (r && this.selections.key(r.id) ? true : false);
39299     },
39300
39301     /**
39302      * Returns True if the specified record id is selected.
39303      * @param {String} id The id of record to check
39304      * @return {Boolean}
39305      */
39306     isIdSelected : function(id){
39307         return (this.selections.key(id) ? true : false);
39308     },
39309
39310     // private
39311     handleMouseDown : function(e, t){
39312         var view = this.grid.getView(), rowIndex;
39313         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39314             return;
39315         };
39316         if(e.shiftKey && this.last !== false){
39317             var last = this.last;
39318             this.selectRange(last, rowIndex, e.ctrlKey);
39319             this.last = last; // reset the last
39320             view.focusRow(rowIndex);
39321         }else{
39322             var isSelected = this.isSelected(rowIndex);
39323             if(e.button !== 0 && isSelected){
39324                 view.focusRow(rowIndex);
39325             }else if(e.ctrlKey && isSelected){
39326                 this.deselectRow(rowIndex);
39327             }else if(!isSelected){
39328                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39329                 view.focusRow(rowIndex);
39330             }
39331         }
39332         this.fireEvent("afterselectionchange", this);
39333     },
39334     // private
39335     handleDragableRowClick :  function(grid, rowIndex, e) 
39336     {
39337         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39338             this.selectRow(rowIndex, false);
39339             grid.view.focusRow(rowIndex);
39340              this.fireEvent("afterselectionchange", this);
39341         }
39342     },
39343     
39344     /**
39345      * Selects multiple rows.
39346      * @param {Array} rows Array of the indexes of the row to select
39347      * @param {Boolean} keepExisting (optional) True to keep existing selections
39348      */
39349     selectRows : function(rows, keepExisting){
39350         if(!keepExisting){
39351             this.clearSelections();
39352         }
39353         for(var i = 0, len = rows.length; i < len; i++){
39354             this.selectRow(rows[i], true);
39355         }
39356     },
39357
39358     /**
39359      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39360      * @param {Number} startRow The index of the first row in the range
39361      * @param {Number} endRow The index of the last row in the range
39362      * @param {Boolean} keepExisting (optional) True to retain existing selections
39363      */
39364     selectRange : function(startRow, endRow, keepExisting){
39365         if(this.locked) return;
39366         if(!keepExisting){
39367             this.clearSelections();
39368         }
39369         if(startRow <= endRow){
39370             for(var i = startRow; i <= endRow; i++){
39371                 this.selectRow(i, true);
39372             }
39373         }else{
39374             for(var i = startRow; i >= endRow; i--){
39375                 this.selectRow(i, true);
39376             }
39377         }
39378     },
39379
39380     /**
39381      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39382      * @param {Number} startRow The index of the first row in the range
39383      * @param {Number} endRow The index of the last row in the range
39384      */
39385     deselectRange : function(startRow, endRow, preventViewNotify){
39386         if(this.locked) return;
39387         for(var i = startRow; i <= endRow; i++){
39388             this.deselectRow(i, preventViewNotify);
39389         }
39390     },
39391
39392     /**
39393      * Selects a row.
39394      * @param {Number} row The index of the row to select
39395      * @param {Boolean} keepExisting (optional) True to keep existing selections
39396      */
39397     selectRow : function(index, keepExisting, preventViewNotify){
39398         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39399         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39400             if(!keepExisting || this.singleSelect){
39401                 this.clearSelections();
39402             }
39403             var r = this.grid.dataSource.getAt(index);
39404             this.selections.add(r);
39405             this.last = this.lastActive = index;
39406             if(!preventViewNotify){
39407                 this.grid.getView().onRowSelect(index);
39408             }
39409             this.fireEvent("rowselect", this, index, r);
39410             this.fireEvent("selectionchange", this);
39411         }
39412     },
39413
39414     /**
39415      * Deselects a row.
39416      * @param {Number} row The index of the row to deselect
39417      */
39418     deselectRow : function(index, preventViewNotify){
39419         if(this.locked) return;
39420         if(this.last == index){
39421             this.last = false;
39422         }
39423         if(this.lastActive == index){
39424             this.lastActive = false;
39425         }
39426         var r = this.grid.dataSource.getAt(index);
39427         this.selections.remove(r);
39428         if(!preventViewNotify){
39429             this.grid.getView().onRowDeselect(index);
39430         }
39431         this.fireEvent("rowdeselect", this, index);
39432         this.fireEvent("selectionchange", this);
39433     },
39434
39435     // private
39436     restoreLast : function(){
39437         if(this._last){
39438             this.last = this._last;
39439         }
39440     },
39441
39442     // private
39443     acceptsNav : function(row, col, cm){
39444         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39445     },
39446
39447     // private
39448     onEditorKey : function(field, e){
39449         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39450         if(k == e.TAB){
39451             e.stopEvent();
39452             ed.completeEdit();
39453             if(e.shiftKey){
39454                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39455             }else{
39456                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39457             }
39458         }else if(k == e.ENTER && !e.ctrlKey){
39459             e.stopEvent();
39460             ed.completeEdit();
39461             if(e.shiftKey){
39462                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39463             }else{
39464                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39465             }
39466         }else if(k == e.ESC){
39467             ed.cancelEdit();
39468         }
39469         if(newCell){
39470             g.startEditing(newCell[0], newCell[1]);
39471         }
39472     }
39473 });/*
39474  * Based on:
39475  * Ext JS Library 1.1.1
39476  * Copyright(c) 2006-2007, Ext JS, LLC.
39477  *
39478  * Originally Released Under LGPL - original licence link has changed is not relivant.
39479  *
39480  * Fork - LGPL
39481  * <script type="text/javascript">
39482  */
39483 /**
39484  * @class Roo.grid.CellSelectionModel
39485  * @extends Roo.grid.AbstractSelectionModel
39486  * This class provides the basic implementation for cell selection in a grid.
39487  * @constructor
39488  * @param {Object} config The object containing the configuration of this model.
39489  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39490  */
39491 Roo.grid.CellSelectionModel = function(config){
39492     Roo.apply(this, config);
39493
39494     this.selection = null;
39495
39496     this.addEvents({
39497         /**
39498              * @event beforerowselect
39499              * Fires before a cell is selected.
39500              * @param {SelectionModel} this
39501              * @param {Number} rowIndex The selected row index
39502              * @param {Number} colIndex The selected cell index
39503              */
39504             "beforecellselect" : true,
39505         /**
39506              * @event cellselect
39507              * Fires when a cell is selected.
39508              * @param {SelectionModel} this
39509              * @param {Number} rowIndex The selected row index
39510              * @param {Number} colIndex The selected cell index
39511              */
39512             "cellselect" : true,
39513         /**
39514              * @event selectionchange
39515              * Fires when the active selection changes.
39516              * @param {SelectionModel} this
39517              * @param {Object} selection null for no selection or an object (o) with two properties
39518                 <ul>
39519                 <li>o.record: the record object for the row the selection is in</li>
39520                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39521                 </ul>
39522              */
39523             "selectionchange" : true,
39524         /**
39525              * @event tabend
39526              * Fires when the tab (or enter) was pressed on the last editable cell
39527              * You can use this to trigger add new row.
39528              * @param {SelectionModel} this
39529              */
39530             "tabend" : true,
39531          /**
39532              * @event beforeeditnext
39533              * Fires before the next editable sell is made active
39534              * You can use this to skip to another cell or fire the tabend
39535              *    if you set cell to false
39536              * @param {Object} eventdata object : { cell : [ row, col ] } 
39537              */
39538             "beforeeditnext" : true
39539     });
39540     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39541 };
39542
39543 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39544     
39545     enter_is_tab: false,
39546
39547     /** @ignore */
39548     initEvents : function(){
39549         this.grid.on("mousedown", this.handleMouseDown, this);
39550         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39551         var view = this.grid.view;
39552         view.on("refresh", this.onViewChange, this);
39553         view.on("rowupdated", this.onRowUpdated, this);
39554         view.on("beforerowremoved", this.clearSelections, this);
39555         view.on("beforerowsinserted", this.clearSelections, this);
39556         if(this.grid.isEditor){
39557             this.grid.on("beforeedit", this.beforeEdit,  this);
39558         }
39559     },
39560
39561         //private
39562     beforeEdit : function(e){
39563         this.select(e.row, e.column, false, true, e.record);
39564     },
39565
39566         //private
39567     onRowUpdated : function(v, index, r){
39568         if(this.selection && this.selection.record == r){
39569             v.onCellSelect(index, this.selection.cell[1]);
39570         }
39571     },
39572
39573         //private
39574     onViewChange : function(){
39575         this.clearSelections(true);
39576     },
39577
39578         /**
39579          * Returns the currently selected cell,.
39580          * @return {Array} The selected cell (row, column) or null if none selected.
39581          */
39582     getSelectedCell : function(){
39583         return this.selection ? this.selection.cell : null;
39584     },
39585
39586     /**
39587      * Clears all selections.
39588      * @param {Boolean} true to prevent the gridview from being notified about the change.
39589      */
39590     clearSelections : function(preventNotify){
39591         var s = this.selection;
39592         if(s){
39593             if(preventNotify !== true){
39594                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39595             }
39596             this.selection = null;
39597             this.fireEvent("selectionchange", this, null);
39598         }
39599     },
39600
39601     /**
39602      * Returns true if there is a selection.
39603      * @return {Boolean}
39604      */
39605     hasSelection : function(){
39606         return this.selection ? true : false;
39607     },
39608
39609     /** @ignore */
39610     handleMouseDown : function(e, t){
39611         var v = this.grid.getView();
39612         if(this.isLocked()){
39613             return;
39614         };
39615         var row = v.findRowIndex(t);
39616         var cell = v.findCellIndex(t);
39617         if(row !== false && cell !== false){
39618             this.select(row, cell);
39619         }
39620     },
39621
39622     /**
39623      * Selects a cell.
39624      * @param {Number} rowIndex
39625      * @param {Number} collIndex
39626      */
39627     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39628         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39629             this.clearSelections();
39630             r = r || this.grid.dataSource.getAt(rowIndex);
39631             this.selection = {
39632                 record : r,
39633                 cell : [rowIndex, colIndex]
39634             };
39635             if(!preventViewNotify){
39636                 var v = this.grid.getView();
39637                 v.onCellSelect(rowIndex, colIndex);
39638                 if(preventFocus !== true){
39639                     v.focusCell(rowIndex, colIndex);
39640                 }
39641             }
39642             this.fireEvent("cellselect", this, rowIndex, colIndex);
39643             this.fireEvent("selectionchange", this, this.selection);
39644         }
39645     },
39646
39647         //private
39648     isSelectable : function(rowIndex, colIndex, cm){
39649         return !cm.isHidden(colIndex);
39650     },
39651
39652     /** @ignore */
39653     handleKeyDown : function(e){
39654         //Roo.log('Cell Sel Model handleKeyDown');
39655         if(!e.isNavKeyPress()){
39656             return;
39657         }
39658         var g = this.grid, s = this.selection;
39659         if(!s){
39660             e.stopEvent();
39661             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39662             if(cell){
39663                 this.select(cell[0], cell[1]);
39664             }
39665             return;
39666         }
39667         var sm = this;
39668         var walk = function(row, col, step){
39669             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39670         };
39671         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39672         var newCell;
39673
39674       
39675
39676         switch(k){
39677             case e.TAB:
39678                 // handled by onEditorKey
39679                 if (g.isEditor && g.editing) {
39680                     return;
39681                 }
39682                 if(e.shiftKey) {
39683                     newCell = walk(r, c-1, -1);
39684                 } else {
39685                     newCell = walk(r, c+1, 1);
39686                 }
39687                 break;
39688             
39689             case e.DOWN:
39690                newCell = walk(r+1, c, 1);
39691                 break;
39692             
39693             case e.UP:
39694                 newCell = walk(r-1, c, -1);
39695                 break;
39696             
39697             case e.RIGHT:
39698                 newCell = walk(r, c+1, 1);
39699                 break;
39700             
39701             case e.LEFT:
39702                 newCell = walk(r, c-1, -1);
39703                 break;
39704             
39705             case e.ENTER:
39706                 
39707                 if(g.isEditor && !g.editing){
39708                    g.startEditing(r, c);
39709                    e.stopEvent();
39710                    return;
39711                 }
39712                 
39713                 
39714              break;
39715         };
39716         if(newCell){
39717             this.select(newCell[0], newCell[1]);
39718             e.stopEvent();
39719             
39720         }
39721     },
39722
39723     acceptsNav : function(row, col, cm){
39724         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39725     },
39726     /**
39727      * Selects a cell.
39728      * @param {Number} field (not used) - as it's normally used as a listener
39729      * @param {Number} e - event - fake it by using
39730      *
39731      * var e = Roo.EventObjectImpl.prototype;
39732      * e.keyCode = e.TAB
39733      *
39734      * 
39735      */
39736     onEditorKey : function(field, e){
39737         
39738         var k = e.getKey(),
39739             newCell,
39740             g = this.grid,
39741             ed = g.activeEditor,
39742             forward = false;
39743         ///Roo.log('onEditorKey' + k);
39744         
39745         
39746         if (this.enter_is_tab && k == e.ENTER) {
39747             k = e.TAB;
39748         }
39749         
39750         if(k == e.TAB){
39751             if(e.shiftKey){
39752                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39753             }else{
39754                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39755                 forward = true;
39756             }
39757             
39758             e.stopEvent();
39759             
39760         } else if(k == e.ENTER &&  !e.ctrlKey){
39761             ed.completeEdit();
39762             e.stopEvent();
39763             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39764         
39765                 } else if(k == e.ESC){
39766             ed.cancelEdit();
39767         }
39768                 
39769         if (newCell) {
39770             var ecall = { cell : newCell, forward : forward };
39771             this.fireEvent('beforeeditnext', ecall );
39772             newCell = ecall.cell;
39773                         forward = ecall.forward;
39774         }
39775                 
39776         if(newCell){
39777             //Roo.log('next cell after edit');
39778             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39779         } else if (forward) {
39780             // tabbed past last
39781             this.fireEvent.defer(100, this, ['tabend',this]);
39782         }
39783     }
39784 });/*
39785  * Based on:
39786  * Ext JS Library 1.1.1
39787  * Copyright(c) 2006-2007, Ext JS, LLC.
39788  *
39789  * Originally Released Under LGPL - original licence link has changed is not relivant.
39790  *
39791  * Fork - LGPL
39792  * <script type="text/javascript">
39793  */
39794  
39795 /**
39796  * @class Roo.grid.EditorGrid
39797  * @extends Roo.grid.Grid
39798  * Class for creating and editable grid.
39799  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39800  * The container MUST have some type of size defined for the grid to fill. The container will be 
39801  * automatically set to position relative if it isn't already.
39802  * @param {Object} dataSource The data model to bind to
39803  * @param {Object} colModel The column model with info about this grid's columns
39804  */
39805 Roo.grid.EditorGrid = function(container, config){
39806     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39807     this.getGridEl().addClass("xedit-grid");
39808
39809     if(!this.selModel){
39810         this.selModel = new Roo.grid.CellSelectionModel();
39811     }
39812
39813     this.activeEditor = null;
39814
39815         this.addEvents({
39816             /**
39817              * @event beforeedit
39818              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39819              * <ul style="padding:5px;padding-left:16px;">
39820              * <li>grid - This grid</li>
39821              * <li>record - The record being edited</li>
39822              * <li>field - The field name being edited</li>
39823              * <li>value - The value for the field being edited.</li>
39824              * <li>row - The grid row index</li>
39825              * <li>column - The grid column index</li>
39826              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39827              * </ul>
39828              * @param {Object} e An edit event (see above for description)
39829              */
39830             "beforeedit" : true,
39831             /**
39832              * @event afteredit
39833              * Fires after a cell is edited. <br />
39834              * <ul style="padding:5px;padding-left:16px;">
39835              * <li>grid - This grid</li>
39836              * <li>record - The record being edited</li>
39837              * <li>field - The field name being edited</li>
39838              * <li>value - The value being set</li>
39839              * <li>originalValue - The original value for the field, before the edit.</li>
39840              * <li>row - The grid row index</li>
39841              * <li>column - The grid column index</li>
39842              * </ul>
39843              * @param {Object} e An edit event (see above for description)
39844              */
39845             "afteredit" : true,
39846             /**
39847              * @event validateedit
39848              * Fires after a cell is edited, but before the value is set in the record. 
39849          * You can use this to modify the value being set in the field, Return false
39850              * to cancel the change. The edit event object has the following properties <br />
39851              * <ul style="padding:5px;padding-left:16px;">
39852          * <li>editor - This editor</li>
39853              * <li>grid - This grid</li>
39854              * <li>record - The record being edited</li>
39855              * <li>field - The field name being edited</li>
39856              * <li>value - The value being set</li>
39857              * <li>originalValue - The original value for the field, before the edit.</li>
39858              * <li>row - The grid row index</li>
39859              * <li>column - The grid column index</li>
39860              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39861              * </ul>
39862              * @param {Object} e An edit event (see above for description)
39863              */
39864             "validateedit" : true
39865         });
39866     this.on("bodyscroll", this.stopEditing,  this);
39867     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39868 };
39869
39870 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39871     /**
39872      * @cfg {Number} clicksToEdit
39873      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39874      */
39875     clicksToEdit: 2,
39876
39877     // private
39878     isEditor : true,
39879     // private
39880     trackMouseOver: false, // causes very odd FF errors
39881
39882     onCellDblClick : function(g, row, col){
39883         this.startEditing(row, col);
39884     },
39885
39886     onEditComplete : function(ed, value, startValue){
39887         this.editing = false;
39888         this.activeEditor = null;
39889         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39890         var r = ed.record;
39891         var field = this.colModel.getDataIndex(ed.col);
39892         var e = {
39893             grid: this,
39894             record: r,
39895             field: field,
39896             originalValue: startValue,
39897             value: value,
39898             row: ed.row,
39899             column: ed.col,
39900             cancel:false,
39901             editor: ed
39902         };
39903         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39904         cell.show();
39905           
39906         if(String(value) !== String(startValue)){
39907             
39908             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39909                 r.set(field, e.value);
39910                 // if we are dealing with a combo box..
39911                 // then we also set the 'name' colum to be the displayField
39912                 if (ed.field.displayField && ed.field.name) {
39913                     r.set(ed.field.name, ed.field.el.dom.value);
39914                 }
39915                 
39916                 delete e.cancel; //?? why!!!
39917                 this.fireEvent("afteredit", e);
39918             }
39919         } else {
39920             this.fireEvent("afteredit", e); // always fire it!
39921         }
39922         this.view.focusCell(ed.row, ed.col);
39923     },
39924
39925     /**
39926      * Starts editing the specified for the specified row/column
39927      * @param {Number} rowIndex
39928      * @param {Number} colIndex
39929      */
39930     startEditing : function(row, col){
39931         this.stopEditing();
39932         if(this.colModel.isCellEditable(col, row)){
39933             this.view.ensureVisible(row, col, true);
39934           
39935             var r = this.dataSource.getAt(row);
39936             var field = this.colModel.getDataIndex(col);
39937             var cell = Roo.get(this.view.getCell(row,col));
39938             var e = {
39939                 grid: this,
39940                 record: r,
39941                 field: field,
39942                 value: r.data[field],
39943                 row: row,
39944                 column: col,
39945                 cancel:false 
39946             };
39947             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39948                 this.editing = true;
39949                 var ed = this.colModel.getCellEditor(col, row);
39950                 
39951                 if (!ed) {
39952                     return;
39953                 }
39954                 if(!ed.rendered){
39955                     ed.render(ed.parentEl || document.body);
39956                 }
39957                 ed.field.reset();
39958                
39959                 cell.hide();
39960                 
39961                 (function(){ // complex but required for focus issues in safari, ie and opera
39962                     ed.row = row;
39963                     ed.col = col;
39964                     ed.record = r;
39965                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39966                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39967                     this.activeEditor = ed;
39968                     var v = r.data[field];
39969                     ed.startEdit(this.view.getCell(row, col), v);
39970                     // combo's with 'displayField and name set
39971                     if (ed.field.displayField && ed.field.name) {
39972                         ed.field.el.dom.value = r.data[ed.field.name];
39973                     }
39974                     
39975                     
39976                 }).defer(50, this);
39977             }
39978         }
39979     },
39980         
39981     /**
39982      * Stops any active editing
39983      */
39984     stopEditing : function(){
39985         if(this.activeEditor){
39986             this.activeEditor.completeEdit();
39987         }
39988         this.activeEditor = null;
39989     },
39990         
39991          /**
39992      * Called to get grid's drag proxy text, by default returns this.ddText.
39993      * @return {String}
39994      */
39995     getDragDropText : function(){
39996         var count = this.selModel.getSelectedCell() ? 1 : 0;
39997         return String.format(this.ddText, count, count == 1 ? '' : 's');
39998     }
39999         
40000 });/*
40001  * Based on:
40002  * Ext JS Library 1.1.1
40003  * Copyright(c) 2006-2007, Ext JS, LLC.
40004  *
40005  * Originally Released Under LGPL - original licence link has changed is not relivant.
40006  *
40007  * Fork - LGPL
40008  * <script type="text/javascript">
40009  */
40010
40011 // private - not really -- you end up using it !
40012 // This is a support class used internally by the Grid components
40013
40014 /**
40015  * @class Roo.grid.GridEditor
40016  * @extends Roo.Editor
40017  * Class for creating and editable grid elements.
40018  * @param {Object} config any settings (must include field)
40019  */
40020 Roo.grid.GridEditor = function(field, config){
40021     if (!config && field.field) {
40022         config = field;
40023         field = Roo.factory(config.field, Roo.form);
40024     }
40025     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40026     field.monitorTab = false;
40027 };
40028
40029 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40030     
40031     /**
40032      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40033      */
40034     
40035     alignment: "tl-tl",
40036     autoSize: "width",
40037     hideEl : false,
40038     cls: "x-small-editor x-grid-editor",
40039     shim:false,
40040     shadow:"frame"
40041 });/*
40042  * Based on:
40043  * Ext JS Library 1.1.1
40044  * Copyright(c) 2006-2007, Ext JS, LLC.
40045  *
40046  * Originally Released Under LGPL - original licence link has changed is not relivant.
40047  *
40048  * Fork - LGPL
40049  * <script type="text/javascript">
40050  */
40051   
40052
40053   
40054 Roo.grid.PropertyRecord = Roo.data.Record.create([
40055     {name:'name',type:'string'},  'value'
40056 ]);
40057
40058
40059 Roo.grid.PropertyStore = function(grid, source){
40060     this.grid = grid;
40061     this.store = new Roo.data.Store({
40062         recordType : Roo.grid.PropertyRecord
40063     });
40064     this.store.on('update', this.onUpdate,  this);
40065     if(source){
40066         this.setSource(source);
40067     }
40068     Roo.grid.PropertyStore.superclass.constructor.call(this);
40069 };
40070
40071
40072
40073 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40074     setSource : function(o){
40075         this.source = o;
40076         this.store.removeAll();
40077         var data = [];
40078         for(var k in o){
40079             if(this.isEditableValue(o[k])){
40080                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40081             }
40082         }
40083         this.store.loadRecords({records: data}, {}, true);
40084     },
40085
40086     onUpdate : function(ds, record, type){
40087         if(type == Roo.data.Record.EDIT){
40088             var v = record.data['value'];
40089             var oldValue = record.modified['value'];
40090             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40091                 this.source[record.id] = v;
40092                 record.commit();
40093                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40094             }else{
40095                 record.reject();
40096             }
40097         }
40098     },
40099
40100     getProperty : function(row){
40101        return this.store.getAt(row);
40102     },
40103
40104     isEditableValue: function(val){
40105         if(val && val instanceof Date){
40106             return true;
40107         }else if(typeof val == 'object' || typeof val == 'function'){
40108             return false;
40109         }
40110         return true;
40111     },
40112
40113     setValue : function(prop, value){
40114         this.source[prop] = value;
40115         this.store.getById(prop).set('value', value);
40116     },
40117
40118     getSource : function(){
40119         return this.source;
40120     }
40121 });
40122
40123 Roo.grid.PropertyColumnModel = function(grid, store){
40124     this.grid = grid;
40125     var g = Roo.grid;
40126     g.PropertyColumnModel.superclass.constructor.call(this, [
40127         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40128         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40129     ]);
40130     this.store = store;
40131     this.bselect = Roo.DomHelper.append(document.body, {
40132         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40133             {tag: 'option', value: 'true', html: 'true'},
40134             {tag: 'option', value: 'false', html: 'false'}
40135         ]
40136     });
40137     Roo.id(this.bselect);
40138     var f = Roo.form;
40139     this.editors = {
40140         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40141         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40142         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40143         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40144         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40145     };
40146     this.renderCellDelegate = this.renderCell.createDelegate(this);
40147     this.renderPropDelegate = this.renderProp.createDelegate(this);
40148 };
40149
40150 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40151     
40152     
40153     nameText : 'Name',
40154     valueText : 'Value',
40155     
40156     dateFormat : 'm/j/Y',
40157     
40158     
40159     renderDate : function(dateVal){
40160         return dateVal.dateFormat(this.dateFormat);
40161     },
40162
40163     renderBool : function(bVal){
40164         return bVal ? 'true' : 'false';
40165     },
40166
40167     isCellEditable : function(colIndex, rowIndex){
40168         return colIndex == 1;
40169     },
40170
40171     getRenderer : function(col){
40172         return col == 1 ?
40173             this.renderCellDelegate : this.renderPropDelegate;
40174     },
40175
40176     renderProp : function(v){
40177         return this.getPropertyName(v);
40178     },
40179
40180     renderCell : function(val){
40181         var rv = val;
40182         if(val instanceof Date){
40183             rv = this.renderDate(val);
40184         }else if(typeof val == 'boolean'){
40185             rv = this.renderBool(val);
40186         }
40187         return Roo.util.Format.htmlEncode(rv);
40188     },
40189
40190     getPropertyName : function(name){
40191         var pn = this.grid.propertyNames;
40192         return pn && pn[name] ? pn[name] : name;
40193     },
40194
40195     getCellEditor : function(colIndex, rowIndex){
40196         var p = this.store.getProperty(rowIndex);
40197         var n = p.data['name'], val = p.data['value'];
40198         
40199         if(typeof(this.grid.customEditors[n]) == 'string'){
40200             return this.editors[this.grid.customEditors[n]];
40201         }
40202         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40203             return this.grid.customEditors[n];
40204         }
40205         if(val instanceof Date){
40206             return this.editors['date'];
40207         }else if(typeof val == 'number'){
40208             return this.editors['number'];
40209         }else if(typeof val == 'boolean'){
40210             return this.editors['boolean'];
40211         }else{
40212             return this.editors['string'];
40213         }
40214     }
40215 });
40216
40217 /**
40218  * @class Roo.grid.PropertyGrid
40219  * @extends Roo.grid.EditorGrid
40220  * This class represents the  interface of a component based property grid control.
40221  * <br><br>Usage:<pre><code>
40222  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40223       
40224  });
40225  // set any options
40226  grid.render();
40227  * </code></pre>
40228   
40229  * @constructor
40230  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40231  * The container MUST have some type of size defined for the grid to fill. The container will be
40232  * automatically set to position relative if it isn't already.
40233  * @param {Object} config A config object that sets properties on this grid.
40234  */
40235 Roo.grid.PropertyGrid = function(container, config){
40236     config = config || {};
40237     var store = new Roo.grid.PropertyStore(this);
40238     this.store = store;
40239     var cm = new Roo.grid.PropertyColumnModel(this, store);
40240     store.store.sort('name', 'ASC');
40241     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40242         ds: store.store,
40243         cm: cm,
40244         enableColLock:false,
40245         enableColumnMove:false,
40246         stripeRows:false,
40247         trackMouseOver: false,
40248         clicksToEdit:1
40249     }, config));
40250     this.getGridEl().addClass('x-props-grid');
40251     this.lastEditRow = null;
40252     this.on('columnresize', this.onColumnResize, this);
40253     this.addEvents({
40254          /**
40255              * @event beforepropertychange
40256              * Fires before a property changes (return false to stop?)
40257              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40258              * @param {String} id Record Id
40259              * @param {String} newval New Value
40260          * @param {String} oldval Old Value
40261              */
40262         "beforepropertychange": true,
40263         /**
40264              * @event propertychange
40265              * Fires after a property changes
40266              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40267              * @param {String} id Record Id
40268              * @param {String} newval New Value
40269          * @param {String} oldval Old Value
40270              */
40271         "propertychange": true
40272     });
40273     this.customEditors = this.customEditors || {};
40274 };
40275 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40276     
40277      /**
40278      * @cfg {Object} customEditors map of colnames=> custom editors.
40279      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40280      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40281      * false disables editing of the field.
40282          */
40283     
40284       /**
40285      * @cfg {Object} propertyNames map of property Names to their displayed value
40286          */
40287     
40288     render : function(){
40289         Roo.grid.PropertyGrid.superclass.render.call(this);
40290         this.autoSize.defer(100, this);
40291     },
40292
40293     autoSize : function(){
40294         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40295         if(this.view){
40296             this.view.fitColumns();
40297         }
40298     },
40299
40300     onColumnResize : function(){
40301         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40302         this.autoSize();
40303     },
40304     /**
40305      * Sets the data for the Grid
40306      * accepts a Key => Value object of all the elements avaiable.
40307      * @param {Object} data  to appear in grid.
40308      */
40309     setSource : function(source){
40310         this.store.setSource(source);
40311         //this.autoSize();
40312     },
40313     /**
40314      * Gets all the data from the grid.
40315      * @return {Object} data  data stored in grid
40316      */
40317     getSource : function(){
40318         return this.store.getSource();
40319     }
40320 });/*
40321   
40322  * Licence LGPL
40323  
40324  */
40325  
40326 /**
40327  * @class Roo.grid.Calendar
40328  * @extends Roo.util.Grid
40329  * This class extends the Grid to provide a calendar widget
40330  * <br><br>Usage:<pre><code>
40331  var grid = new Roo.grid.Calendar("my-container-id", {
40332      ds: myDataStore,
40333      cm: myColModel,
40334      selModel: mySelectionModel,
40335      autoSizeColumns: true,
40336      monitorWindowResize: false,
40337      trackMouseOver: true
40338      eventstore : real data store..
40339  });
40340  // set any options
40341  grid.render();
40342   
40343   * @constructor
40344  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40345  * The container MUST have some type of size defined for the grid to fill. The container will be
40346  * automatically set to position relative if it isn't already.
40347  * @param {Object} config A config object that sets properties on this grid.
40348  */
40349 Roo.grid.Calendar = function(container, config){
40350         // initialize the container
40351         this.container = Roo.get(container);
40352         this.container.update("");
40353         this.container.setStyle("overflow", "hidden");
40354     this.container.addClass('x-grid-container');
40355
40356     this.id = this.container.id;
40357
40358     Roo.apply(this, config);
40359     // check and correct shorthanded configs
40360     
40361     var rows = [];
40362     var d =1;
40363     for (var r = 0;r < 6;r++) {
40364         
40365         rows[r]=[];
40366         for (var c =0;c < 7;c++) {
40367             rows[r][c]= '';
40368         }
40369     }
40370     if (this.eventStore) {
40371         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40372         this.eventStore.on('load',this.onLoad, this);
40373         this.eventStore.on('beforeload',this.clearEvents, this);
40374          
40375     }
40376     
40377     this.dataSource = new Roo.data.Store({
40378             proxy: new Roo.data.MemoryProxy(rows),
40379             reader: new Roo.data.ArrayReader({}, [
40380                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40381     });
40382
40383     this.dataSource.load();
40384     this.ds = this.dataSource;
40385     this.ds.xmodule = this.xmodule || false;
40386     
40387     
40388     var cellRender = function(v,x,r)
40389     {
40390         return String.format(
40391             '<div class="fc-day  fc-widget-content"><div>' +
40392                 '<div class="fc-event-container"></div>' +
40393                 '<div class="fc-day-number">{0}</div>'+
40394                 
40395                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40396             '</div></div>', v);
40397     
40398     }
40399     
40400     
40401     this.colModel = new Roo.grid.ColumnModel( [
40402         {
40403             xtype: 'ColumnModel',
40404             xns: Roo.grid,
40405             dataIndex : 'weekday0',
40406             header : 'Sunday',
40407             renderer : cellRender
40408         },
40409         {
40410             xtype: 'ColumnModel',
40411             xns: Roo.grid,
40412             dataIndex : 'weekday1',
40413             header : 'Monday',
40414             renderer : cellRender
40415         },
40416         {
40417             xtype: 'ColumnModel',
40418             xns: Roo.grid,
40419             dataIndex : 'weekday2',
40420             header : 'Tuesday',
40421             renderer : cellRender
40422         },
40423         {
40424             xtype: 'ColumnModel',
40425             xns: Roo.grid,
40426             dataIndex : 'weekday3',
40427             header : 'Wednesday',
40428             renderer : cellRender
40429         },
40430         {
40431             xtype: 'ColumnModel',
40432             xns: Roo.grid,
40433             dataIndex : 'weekday4',
40434             header : 'Thursday',
40435             renderer : cellRender
40436         },
40437         {
40438             xtype: 'ColumnModel',
40439             xns: Roo.grid,
40440             dataIndex : 'weekday5',
40441             header : 'Friday',
40442             renderer : cellRender
40443         },
40444         {
40445             xtype: 'ColumnModel',
40446             xns: Roo.grid,
40447             dataIndex : 'weekday6',
40448             header : 'Saturday',
40449             renderer : cellRender
40450         }
40451     ]);
40452     this.cm = this.colModel;
40453     this.cm.xmodule = this.xmodule || false;
40454  
40455         
40456           
40457     //this.selModel = new Roo.grid.CellSelectionModel();
40458     //this.sm = this.selModel;
40459     //this.selModel.init(this);
40460     
40461     
40462     if(this.width){
40463         this.container.setWidth(this.width);
40464     }
40465
40466     if(this.height){
40467         this.container.setHeight(this.height);
40468     }
40469     /** @private */
40470         this.addEvents({
40471         // raw events
40472         /**
40473          * @event click
40474          * The raw click event for the entire grid.
40475          * @param {Roo.EventObject} e
40476          */
40477         "click" : true,
40478         /**
40479          * @event dblclick
40480          * The raw dblclick event for the entire grid.
40481          * @param {Roo.EventObject} e
40482          */
40483         "dblclick" : true,
40484         /**
40485          * @event contextmenu
40486          * The raw contextmenu event for the entire grid.
40487          * @param {Roo.EventObject} e
40488          */
40489         "contextmenu" : true,
40490         /**
40491          * @event mousedown
40492          * The raw mousedown event for the entire grid.
40493          * @param {Roo.EventObject} e
40494          */
40495         "mousedown" : true,
40496         /**
40497          * @event mouseup
40498          * The raw mouseup event for the entire grid.
40499          * @param {Roo.EventObject} e
40500          */
40501         "mouseup" : true,
40502         /**
40503          * @event mouseover
40504          * The raw mouseover event for the entire grid.
40505          * @param {Roo.EventObject} e
40506          */
40507         "mouseover" : true,
40508         /**
40509          * @event mouseout
40510          * The raw mouseout event for the entire grid.
40511          * @param {Roo.EventObject} e
40512          */
40513         "mouseout" : true,
40514         /**
40515          * @event keypress
40516          * The raw keypress event for the entire grid.
40517          * @param {Roo.EventObject} e
40518          */
40519         "keypress" : true,
40520         /**
40521          * @event keydown
40522          * The raw keydown event for the entire grid.
40523          * @param {Roo.EventObject} e
40524          */
40525         "keydown" : true,
40526
40527         // custom events
40528
40529         /**
40530          * @event cellclick
40531          * Fires when a cell is clicked
40532          * @param {Grid} this
40533          * @param {Number} rowIndex
40534          * @param {Number} columnIndex
40535          * @param {Roo.EventObject} e
40536          */
40537         "cellclick" : true,
40538         /**
40539          * @event celldblclick
40540          * Fires when a cell is double clicked
40541          * @param {Grid} this
40542          * @param {Number} rowIndex
40543          * @param {Number} columnIndex
40544          * @param {Roo.EventObject} e
40545          */
40546         "celldblclick" : true,
40547         /**
40548          * @event rowclick
40549          * Fires when a row is clicked
40550          * @param {Grid} this
40551          * @param {Number} rowIndex
40552          * @param {Roo.EventObject} e
40553          */
40554         "rowclick" : true,
40555         /**
40556          * @event rowdblclick
40557          * Fires when a row is double clicked
40558          * @param {Grid} this
40559          * @param {Number} rowIndex
40560          * @param {Roo.EventObject} e
40561          */
40562         "rowdblclick" : true,
40563         /**
40564          * @event headerclick
40565          * Fires when a header is clicked
40566          * @param {Grid} this
40567          * @param {Number} columnIndex
40568          * @param {Roo.EventObject} e
40569          */
40570         "headerclick" : true,
40571         /**
40572          * @event headerdblclick
40573          * Fires when a header cell is double clicked
40574          * @param {Grid} this
40575          * @param {Number} columnIndex
40576          * @param {Roo.EventObject} e
40577          */
40578         "headerdblclick" : true,
40579         /**
40580          * @event rowcontextmenu
40581          * Fires when a row is right clicked
40582          * @param {Grid} this
40583          * @param {Number} rowIndex
40584          * @param {Roo.EventObject} e
40585          */
40586         "rowcontextmenu" : true,
40587         /**
40588          * @event cellcontextmenu
40589          * Fires when a cell is right clicked
40590          * @param {Grid} this
40591          * @param {Number} rowIndex
40592          * @param {Number} cellIndex
40593          * @param {Roo.EventObject} e
40594          */
40595          "cellcontextmenu" : true,
40596         /**
40597          * @event headercontextmenu
40598          * Fires when a header is right clicked
40599          * @param {Grid} this
40600          * @param {Number} columnIndex
40601          * @param {Roo.EventObject} e
40602          */
40603         "headercontextmenu" : true,
40604         /**
40605          * @event bodyscroll
40606          * Fires when the body element is scrolled
40607          * @param {Number} scrollLeft
40608          * @param {Number} scrollTop
40609          */
40610         "bodyscroll" : true,
40611         /**
40612          * @event columnresize
40613          * Fires when the user resizes a column
40614          * @param {Number} columnIndex
40615          * @param {Number} newSize
40616          */
40617         "columnresize" : true,
40618         /**
40619          * @event columnmove
40620          * Fires when the user moves a column
40621          * @param {Number} oldIndex
40622          * @param {Number} newIndex
40623          */
40624         "columnmove" : true,
40625         /**
40626          * @event startdrag
40627          * Fires when row(s) start being dragged
40628          * @param {Grid} this
40629          * @param {Roo.GridDD} dd The drag drop object
40630          * @param {event} e The raw browser event
40631          */
40632         "startdrag" : true,
40633         /**
40634          * @event enddrag
40635          * Fires when a drag operation is complete
40636          * @param {Grid} this
40637          * @param {Roo.GridDD} dd The drag drop object
40638          * @param {event} e The raw browser event
40639          */
40640         "enddrag" : true,
40641         /**
40642          * @event dragdrop
40643          * Fires when dragged row(s) are dropped on a valid DD target
40644          * @param {Grid} this
40645          * @param {Roo.GridDD} dd The drag drop object
40646          * @param {String} targetId The target drag drop object
40647          * @param {event} e The raw browser event
40648          */
40649         "dragdrop" : true,
40650         /**
40651          * @event dragover
40652          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40653          * @param {Grid} this
40654          * @param {Roo.GridDD} dd The drag drop object
40655          * @param {String} targetId The target drag drop object
40656          * @param {event} e The raw browser event
40657          */
40658         "dragover" : true,
40659         /**
40660          * @event dragenter
40661          *  Fires when the dragged row(s) first cross another DD target while being dragged
40662          * @param {Grid} this
40663          * @param {Roo.GridDD} dd The drag drop object
40664          * @param {String} targetId The target drag drop object
40665          * @param {event} e The raw browser event
40666          */
40667         "dragenter" : true,
40668         /**
40669          * @event dragout
40670          * Fires when the dragged row(s) leave another DD target while being dragged
40671          * @param {Grid} this
40672          * @param {Roo.GridDD} dd The drag drop object
40673          * @param {String} targetId The target drag drop object
40674          * @param {event} e The raw browser event
40675          */
40676         "dragout" : true,
40677         /**
40678          * @event rowclass
40679          * Fires when a row is rendered, so you can change add a style to it.
40680          * @param {GridView} gridview   The grid view
40681          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40682          */
40683         'rowclass' : true,
40684
40685         /**
40686          * @event render
40687          * Fires when the grid is rendered
40688          * @param {Grid} grid
40689          */
40690         'render' : true,
40691             /**
40692              * @event select
40693              * Fires when a date is selected
40694              * @param {DatePicker} this
40695              * @param {Date} date The selected date
40696              */
40697         'select': true,
40698         /**
40699              * @event monthchange
40700              * Fires when the displayed month changes 
40701              * @param {DatePicker} this
40702              * @param {Date} date The selected month
40703              */
40704         'monthchange': true,
40705         /**
40706              * @event evententer
40707              * Fires when mouse over an event
40708              * @param {Calendar} this
40709              * @param {event} Event
40710              */
40711         'evententer': true,
40712         /**
40713              * @event eventleave
40714              * Fires when the mouse leaves an
40715              * @param {Calendar} this
40716              * @param {event}
40717              */
40718         'eventleave': true,
40719         /**
40720              * @event eventclick
40721              * Fires when the mouse click an
40722              * @param {Calendar} this
40723              * @param {event}
40724              */
40725         'eventclick': true,
40726         /**
40727              * @event eventrender
40728              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40729              * @param {Calendar} this
40730              * @param {data} data to be modified
40731              */
40732         'eventrender': true
40733         
40734     });
40735
40736     Roo.grid.Grid.superclass.constructor.call(this);
40737     this.on('render', function() {
40738         this.view.el.addClass('x-grid-cal'); 
40739         
40740         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40741
40742     },this);
40743     
40744     if (!Roo.grid.Calendar.style) {
40745         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40746             
40747             
40748             '.x-grid-cal .x-grid-col' :  {
40749                 height: 'auto !important',
40750                 'vertical-align': 'top'
40751             },
40752             '.x-grid-cal  .fc-event-hori' : {
40753                 height: '14px'
40754             }
40755              
40756             
40757         }, Roo.id());
40758     }
40759
40760     
40761     
40762 };
40763 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40764     /**
40765      * @cfg {Store} eventStore The store that loads events.
40766      */
40767     eventStore : 25,
40768
40769      
40770     activeDate : false,
40771     startDay : 0,
40772     autoWidth : true,
40773     monitorWindowResize : false,
40774
40775     
40776     resizeColumns : function() {
40777         var col = (this.view.el.getWidth() / 7) - 3;
40778         // loop through cols, and setWidth
40779         for(var i =0 ; i < 7 ; i++){
40780             this.cm.setColumnWidth(i, col);
40781         }
40782     },
40783      setDate :function(date) {
40784         
40785         Roo.log('setDate?');
40786         
40787         this.resizeColumns();
40788         var vd = this.activeDate;
40789         this.activeDate = date;
40790 //        if(vd && this.el){
40791 //            var t = date.getTime();
40792 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40793 //                Roo.log('using add remove');
40794 //                
40795 //                this.fireEvent('monthchange', this, date);
40796 //                
40797 //                this.cells.removeClass("fc-state-highlight");
40798 //                this.cells.each(function(c){
40799 //                   if(c.dateValue == t){
40800 //                       c.addClass("fc-state-highlight");
40801 //                       setTimeout(function(){
40802 //                            try{c.dom.firstChild.focus();}catch(e){}
40803 //                       }, 50);
40804 //                       return false;
40805 //                   }
40806 //                   return true;
40807 //                });
40808 //                return;
40809 //            }
40810 //        }
40811         
40812         var days = date.getDaysInMonth();
40813         
40814         var firstOfMonth = date.getFirstDateOfMonth();
40815         var startingPos = firstOfMonth.getDay()-this.startDay;
40816         
40817         if(startingPos < this.startDay){
40818             startingPos += 7;
40819         }
40820         
40821         var pm = date.add(Date.MONTH, -1);
40822         var prevStart = pm.getDaysInMonth()-startingPos;
40823 //        
40824         
40825         
40826         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40827         
40828         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40829         //this.cells.addClassOnOver('fc-state-hover');
40830         
40831         var cells = this.cells.elements;
40832         var textEls = this.textNodes;
40833         
40834         //Roo.each(cells, function(cell){
40835         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40836         //});
40837         
40838         days += startingPos;
40839
40840         // convert everything to numbers so it's fast
40841         var day = 86400000;
40842         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40843         //Roo.log(d);
40844         //Roo.log(pm);
40845         //Roo.log(prevStart);
40846         
40847         var today = new Date().clearTime().getTime();
40848         var sel = date.clearTime().getTime();
40849         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40850         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40851         var ddMatch = this.disabledDatesRE;
40852         var ddText = this.disabledDatesText;
40853         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40854         var ddaysText = this.disabledDaysText;
40855         var format = this.format;
40856         
40857         var setCellClass = function(cal, cell){
40858             
40859             //Roo.log('set Cell Class');
40860             cell.title = "";
40861             var t = d.getTime();
40862             
40863             //Roo.log(d);
40864             
40865             
40866             cell.dateValue = t;
40867             if(t == today){
40868                 cell.className += " fc-today";
40869                 cell.className += " fc-state-highlight";
40870                 cell.title = cal.todayText;
40871             }
40872             if(t == sel){
40873                 // disable highlight in other month..
40874                 cell.className += " fc-state-highlight";
40875                 
40876             }
40877             // disabling
40878             if(t < min) {
40879                 //cell.className = " fc-state-disabled";
40880                 cell.title = cal.minText;
40881                 return;
40882             }
40883             if(t > max) {
40884                 //cell.className = " fc-state-disabled";
40885                 cell.title = cal.maxText;
40886                 return;
40887             }
40888             if(ddays){
40889                 if(ddays.indexOf(d.getDay()) != -1){
40890                     // cell.title = ddaysText;
40891                    // cell.className = " fc-state-disabled";
40892                 }
40893             }
40894             if(ddMatch && format){
40895                 var fvalue = d.dateFormat(format);
40896                 if(ddMatch.test(fvalue)){
40897                     cell.title = ddText.replace("%0", fvalue);
40898                    cell.className = " fc-state-disabled";
40899                 }
40900             }
40901             
40902             if (!cell.initialClassName) {
40903                 cell.initialClassName = cell.dom.className;
40904             }
40905             
40906             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40907         };
40908
40909         var i = 0;
40910         
40911         for(; i < startingPos; i++) {
40912             cells[i].dayName =  (++prevStart);
40913             Roo.log(textEls[i]);
40914             d.setDate(d.getDate()+1);
40915             
40916             //cells[i].className = "fc-past fc-other-month";
40917             setCellClass(this, cells[i]);
40918         }
40919         
40920         var intDay = 0;
40921         
40922         for(; i < days; i++){
40923             intDay = i - startingPos + 1;
40924             cells[i].dayName =  (intDay);
40925             d.setDate(d.getDate()+1);
40926             
40927             cells[i].className = ''; // "x-date-active";
40928             setCellClass(this, cells[i]);
40929         }
40930         var extraDays = 0;
40931         
40932         for(; i < 42; i++) {
40933             //textEls[i].innerHTML = (++extraDays);
40934             
40935             d.setDate(d.getDate()+1);
40936             cells[i].dayName = (++extraDays);
40937             cells[i].className = "fc-future fc-other-month";
40938             setCellClass(this, cells[i]);
40939         }
40940         
40941         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40942         
40943         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40944         
40945         // this will cause all the cells to mis
40946         var rows= [];
40947         var i =0;
40948         for (var r = 0;r < 6;r++) {
40949             for (var c =0;c < 7;c++) {
40950                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40951             }    
40952         }
40953         
40954         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40955         for(i=0;i<cells.length;i++) {
40956             
40957             this.cells.elements[i].dayName = cells[i].dayName ;
40958             this.cells.elements[i].className = cells[i].className;
40959             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40960             this.cells.elements[i].title = cells[i].title ;
40961             this.cells.elements[i].dateValue = cells[i].dateValue ;
40962         }
40963         
40964         
40965         
40966         
40967         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40968         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40969         
40970         ////if(totalRows != 6){
40971             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40972            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40973        // }
40974         
40975         this.fireEvent('monthchange', this, date);
40976         
40977         
40978     },
40979  /**
40980      * Returns the grid's SelectionModel.
40981      * @return {SelectionModel}
40982      */
40983     getSelectionModel : function(){
40984         if(!this.selModel){
40985             this.selModel = new Roo.grid.CellSelectionModel();
40986         }
40987         return this.selModel;
40988     },
40989
40990     load: function() {
40991         this.eventStore.load()
40992         
40993         
40994         
40995     },
40996     
40997     findCell : function(dt) {
40998         dt = dt.clearTime().getTime();
40999         var ret = false;
41000         this.cells.each(function(c){
41001             //Roo.log("check " +c.dateValue + '?=' + dt);
41002             if(c.dateValue == dt){
41003                 ret = c;
41004                 return false;
41005             }
41006             return true;
41007         });
41008         
41009         return ret;
41010     },
41011     
41012     findCells : function(rec) {
41013         var s = rec.data.start_dt.clone().clearTime().getTime();
41014        // Roo.log(s);
41015         var e= rec.data.end_dt.clone().clearTime().getTime();
41016        // Roo.log(e);
41017         var ret = [];
41018         this.cells.each(function(c){
41019              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41020             
41021             if(c.dateValue > e){
41022                 return ;
41023             }
41024             if(c.dateValue < s){
41025                 return ;
41026             }
41027             ret.push(c);
41028         });
41029         
41030         return ret;    
41031     },
41032     
41033     findBestRow: function(cells)
41034     {
41035         var ret = 0;
41036         
41037         for (var i =0 ; i < cells.length;i++) {
41038             ret  = Math.max(cells[i].rows || 0,ret);
41039         }
41040         return ret;
41041         
41042     },
41043     
41044     
41045     addItem : function(rec)
41046     {
41047         // look for vertical location slot in
41048         var cells = this.findCells(rec);
41049         
41050         rec.row = this.findBestRow(cells);
41051         
41052         // work out the location.
41053         
41054         var crow = false;
41055         var rows = [];
41056         for(var i =0; i < cells.length; i++) {
41057             if (!crow) {
41058                 crow = {
41059                     start : cells[i],
41060                     end :  cells[i]
41061                 };
41062                 continue;
41063             }
41064             if (crow.start.getY() == cells[i].getY()) {
41065                 // on same row.
41066                 crow.end = cells[i];
41067                 continue;
41068             }
41069             // different row.
41070             rows.push(crow);
41071             crow = {
41072                 start: cells[i],
41073                 end : cells[i]
41074             };
41075             
41076         }
41077         
41078         rows.push(crow);
41079         rec.els = [];
41080         rec.rows = rows;
41081         rec.cells = cells;
41082         for (var i = 0; i < cells.length;i++) {
41083             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41084             
41085         }
41086         
41087         
41088     },
41089     
41090     clearEvents: function() {
41091         
41092         if (!this.eventStore.getCount()) {
41093             return;
41094         }
41095         // reset number of rows in cells.
41096         Roo.each(this.cells.elements, function(c){
41097             c.rows = 0;
41098         });
41099         
41100         this.eventStore.each(function(e) {
41101             this.clearEvent(e);
41102         },this);
41103         
41104     },
41105     
41106     clearEvent : function(ev)
41107     {
41108         if (ev.els) {
41109             Roo.each(ev.els, function(el) {
41110                 el.un('mouseenter' ,this.onEventEnter, this);
41111                 el.un('mouseleave' ,this.onEventLeave, this);
41112                 el.remove();
41113             },this);
41114             ev.els = [];
41115         }
41116     },
41117     
41118     
41119     renderEvent : function(ev,ctr) {
41120         if (!ctr) {
41121              ctr = this.view.el.select('.fc-event-container',true).first();
41122         }
41123         
41124          
41125         this.clearEvent(ev);
41126             //code
41127        
41128         
41129         
41130         ev.els = [];
41131         var cells = ev.cells;
41132         var rows = ev.rows;
41133         this.fireEvent('eventrender', this, ev);
41134         
41135         for(var i =0; i < rows.length; i++) {
41136             
41137             cls = '';
41138             if (i == 0) {
41139                 cls += ' fc-event-start';
41140             }
41141             if ((i+1) == rows.length) {
41142                 cls += ' fc-event-end';
41143             }
41144             
41145             //Roo.log(ev.data);
41146             // how many rows should it span..
41147             var cg = this.eventTmpl.append(ctr,Roo.apply({
41148                 fccls : cls
41149                 
41150             }, ev.data) , true);
41151             
41152             
41153             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41154             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41155             cg.on('click', this.onEventClick, this, ev);
41156             
41157             ev.els.push(cg);
41158             
41159             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41160             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41161             //Roo.log(cg);
41162              
41163             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41164             cg.setWidth(ebox.right - sbox.x -2);
41165         }
41166     },
41167     
41168     renderEvents: function()
41169     {   
41170         // first make sure there is enough space..
41171         
41172         if (!this.eventTmpl) {
41173             this.eventTmpl = new Roo.Template(
41174                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41175                     '<div class="fc-event-inner">' +
41176                         '<span class="fc-event-time">{time}</span>' +
41177                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41178                     '</div>' +
41179                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41180                 '</div>'
41181             );
41182                 
41183         }
41184                
41185         
41186         
41187         this.cells.each(function(c) {
41188             //Roo.log(c.select('.fc-day-content div',true).first());
41189             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41190         });
41191         
41192         var ctr = this.view.el.select('.fc-event-container',true).first();
41193         
41194         var cls;
41195         this.eventStore.each(function(ev){
41196             
41197             this.renderEvent(ev);
41198              
41199              
41200         }, this);
41201         this.view.layout();
41202         
41203     },
41204     
41205     onEventEnter: function (e, el,event,d) {
41206         this.fireEvent('evententer', this, el, event);
41207     },
41208     
41209     onEventLeave: function (e, el,event,d) {
41210         this.fireEvent('eventleave', this, el, event);
41211     },
41212     
41213     onEventClick: function (e, el,event,d) {
41214         this.fireEvent('eventclick', this, el, event);
41215     },
41216     
41217     onMonthChange: function () {
41218         this.store.load();
41219     },
41220     
41221     onLoad: function () {
41222         
41223         //Roo.log('calendar onload');
41224 //         
41225         if(this.eventStore.getCount() > 0){
41226             
41227            
41228             
41229             this.eventStore.each(function(d){
41230                 
41231                 
41232                 // FIXME..
41233                 var add =   d.data;
41234                 if (typeof(add.end_dt) == 'undefined')  {
41235                     Roo.log("Missing End time in calendar data: ");
41236                     Roo.log(d);
41237                     return;
41238                 }
41239                 if (typeof(add.start_dt) == 'undefined')  {
41240                     Roo.log("Missing Start time in calendar data: ");
41241                     Roo.log(d);
41242                     return;
41243                 }
41244                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41245                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41246                 add.id = add.id || d.id;
41247                 add.title = add.title || '??';
41248                 
41249                 this.addItem(d);
41250                 
41251              
41252             },this);
41253         }
41254         
41255         this.renderEvents();
41256     }
41257     
41258
41259 });
41260 /*
41261  grid : {
41262                 xtype: 'Grid',
41263                 xns: Roo.grid,
41264                 listeners : {
41265                     render : function ()
41266                     {
41267                         _this.grid = this;
41268                         
41269                         if (!this.view.el.hasClass('course-timesheet')) {
41270                             this.view.el.addClass('course-timesheet');
41271                         }
41272                         if (this.tsStyle) {
41273                             this.ds.load({});
41274                             return; 
41275                         }
41276                         Roo.log('width');
41277                         Roo.log(_this.grid.view.el.getWidth());
41278                         
41279                         
41280                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41281                             '.course-timesheet .x-grid-row' : {
41282                                 height: '80px'
41283                             },
41284                             '.x-grid-row td' : {
41285                                 'vertical-align' : 0
41286                             },
41287                             '.course-edit-link' : {
41288                                 'color' : 'blue',
41289                                 'text-overflow' : 'ellipsis',
41290                                 'overflow' : 'hidden',
41291                                 'white-space' : 'nowrap',
41292                                 'cursor' : 'pointer'
41293                             },
41294                             '.sub-link' : {
41295                                 'color' : 'green'
41296                             },
41297                             '.de-act-sup-link' : {
41298                                 'color' : 'purple',
41299                                 'text-decoration' : 'line-through'
41300                             },
41301                             '.de-act-link' : {
41302                                 'color' : 'red',
41303                                 'text-decoration' : 'line-through'
41304                             },
41305                             '.course-timesheet .course-highlight' : {
41306                                 'border-top-style': 'dashed !important',
41307                                 'border-bottom-bottom': 'dashed !important'
41308                             },
41309                             '.course-timesheet .course-item' : {
41310                                 'font-family'   : 'tahoma, arial, helvetica',
41311                                 'font-size'     : '11px',
41312                                 'overflow'      : 'hidden',
41313                                 'padding-left'  : '10px',
41314                                 'padding-right' : '10px',
41315                                 'padding-top' : '10px' 
41316                             }
41317                             
41318                         }, Roo.id());
41319                                 this.ds.load({});
41320                     }
41321                 },
41322                 autoWidth : true,
41323                 monitorWindowResize : false,
41324                 cellrenderer : function(v,x,r)
41325                 {
41326                     return v;
41327                 },
41328                 sm : {
41329                     xtype: 'CellSelectionModel',
41330                     xns: Roo.grid
41331                 },
41332                 dataSource : {
41333                     xtype: 'Store',
41334                     xns: Roo.data,
41335                     listeners : {
41336                         beforeload : function (_self, options)
41337                         {
41338                             options.params = options.params || {};
41339                             options.params._month = _this.monthField.getValue();
41340                             options.params.limit = 9999;
41341                             options.params['sort'] = 'when_dt';    
41342                             options.params['dir'] = 'ASC';    
41343                             this.proxy.loadResponse = this.loadResponse;
41344                             Roo.log("load?");
41345                             //this.addColumns();
41346                         },
41347                         load : function (_self, records, options)
41348                         {
41349                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41350                                 // if you click on the translation.. you can edit it...
41351                                 var el = Roo.get(this);
41352                                 var id = el.dom.getAttribute('data-id');
41353                                 var d = el.dom.getAttribute('data-date');
41354                                 var t = el.dom.getAttribute('data-time');
41355                                 //var id = this.child('span').dom.textContent;
41356                                 
41357                                 //Roo.log(this);
41358                                 Pman.Dialog.CourseCalendar.show({
41359                                     id : id,
41360                                     when_d : d,
41361                                     when_t : t,
41362                                     productitem_active : id ? 1 : 0
41363                                 }, function() {
41364                                     _this.grid.ds.load({});
41365                                 });
41366                            
41367                            });
41368                            
41369                            _this.panel.fireEvent('resize', [ '', '' ]);
41370                         }
41371                     },
41372                     loadResponse : function(o, success, response){
41373                             // this is overridden on before load..
41374                             
41375                             Roo.log("our code?");       
41376                             //Roo.log(success);
41377                             //Roo.log(response)
41378                             delete this.activeRequest;
41379                             if(!success){
41380                                 this.fireEvent("loadexception", this, o, response);
41381                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41382                                 return;
41383                             }
41384                             var result;
41385                             try {
41386                                 result = o.reader.read(response);
41387                             }catch(e){
41388                                 Roo.log("load exception?");
41389                                 this.fireEvent("loadexception", this, o, response, e);
41390                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41391                                 return;
41392                             }
41393                             Roo.log("ready...");        
41394                             // loop through result.records;
41395                             // and set this.tdate[date] = [] << array of records..
41396                             _this.tdata  = {};
41397                             Roo.each(result.records, function(r){
41398                                 //Roo.log(r.data);
41399                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41400                                     _this.tdata[r.data.when_dt.format('j')] = [];
41401                                 }
41402                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41403                             });
41404                             
41405                             //Roo.log(_this.tdata);
41406                             
41407                             result.records = [];
41408                             result.totalRecords = 6;
41409                     
41410                             // let's generate some duumy records for the rows.
41411                             //var st = _this.dateField.getValue();
41412                             
41413                             // work out monday..
41414                             //st = st.add(Date.DAY, -1 * st.format('w'));
41415                             
41416                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41417                             
41418                             var firstOfMonth = date.getFirstDayOfMonth();
41419                             var days = date.getDaysInMonth();
41420                             var d = 1;
41421                             var firstAdded = false;
41422                             for (var i = 0; i < result.totalRecords ; i++) {
41423                                 //var d= st.add(Date.DAY, i);
41424                                 var row = {};
41425                                 var added = 0;
41426                                 for(var w = 0 ; w < 7 ; w++){
41427                                     if(!firstAdded && firstOfMonth != w){
41428                                         continue;
41429                                     }
41430                                     if(d > days){
41431                                         continue;
41432                                     }
41433                                     firstAdded = true;
41434                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41435                                     row['weekday'+w] = String.format(
41436                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41437                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41438                                                     d,
41439                                                     date.format('Y-m-')+dd
41440                                                 );
41441                                     added++;
41442                                     if(typeof(_this.tdata[d]) != 'undefined'){
41443                                         Roo.each(_this.tdata[d], function(r){
41444                                             var is_sub = '';
41445                                             var deactive = '';
41446                                             var id = r.id;
41447                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41448                                             if(r.parent_id*1>0){
41449                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41450                                                 id = r.parent_id;
41451                                             }
41452                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41453                                                 deactive = 'de-act-link';
41454                                             }
41455                                             
41456                                             row['weekday'+w] += String.format(
41457                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41458                                                     id, //0
41459                                                     r.product_id_name, //1
41460                                                     r.when_dt.format('h:ia'), //2
41461                                                     is_sub, //3
41462                                                     deactive, //4
41463                                                     desc // 5
41464                                             );
41465                                         });
41466                                     }
41467                                     d++;
41468                                 }
41469                                 
41470                                 // only do this if something added..
41471                                 if(added > 0){ 
41472                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41473                                 }
41474                                 
41475                                 
41476                                 // push it twice. (second one with an hour..
41477                                 
41478                             }
41479                             //Roo.log(result);
41480                             this.fireEvent("load", this, o, o.request.arg);
41481                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41482                         },
41483                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41484                     proxy : {
41485                         xtype: 'HttpProxy',
41486                         xns: Roo.data,
41487                         method : 'GET',
41488                         url : baseURL + '/Roo/Shop_course.php'
41489                     },
41490                     reader : {
41491                         xtype: 'JsonReader',
41492                         xns: Roo.data,
41493                         id : 'id',
41494                         fields : [
41495                             {
41496                                 'name': 'id',
41497                                 'type': 'int'
41498                             },
41499                             {
41500                                 'name': 'when_dt',
41501                                 'type': 'string'
41502                             },
41503                             {
41504                                 'name': 'end_dt',
41505                                 'type': 'string'
41506                             },
41507                             {
41508                                 'name': 'parent_id',
41509                                 'type': 'int'
41510                             },
41511                             {
41512                                 'name': 'product_id',
41513                                 'type': 'int'
41514                             },
41515                             {
41516                                 'name': 'productitem_id',
41517                                 'type': 'int'
41518                             },
41519                             {
41520                                 'name': 'guid',
41521                                 'type': 'int'
41522                             }
41523                         ]
41524                     }
41525                 },
41526                 toolbar : {
41527                     xtype: 'Toolbar',
41528                     xns: Roo,
41529                     items : [
41530                         {
41531                             xtype: 'Button',
41532                             xns: Roo.Toolbar,
41533                             listeners : {
41534                                 click : function (_self, e)
41535                                 {
41536                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41537                                     sd.setMonth(sd.getMonth()-1);
41538                                     _this.monthField.setValue(sd.format('Y-m-d'));
41539                                     _this.grid.ds.load({});
41540                                 }
41541                             },
41542                             text : "Back"
41543                         },
41544                         {
41545                             xtype: 'Separator',
41546                             xns: Roo.Toolbar
41547                         },
41548                         {
41549                             xtype: 'MonthField',
41550                             xns: Roo.form,
41551                             listeners : {
41552                                 render : function (_self)
41553                                 {
41554                                     _this.monthField = _self;
41555                                    // _this.monthField.set  today
41556                                 },
41557                                 select : function (combo, date)
41558                                 {
41559                                     _this.grid.ds.load({});
41560                                 }
41561                             },
41562                             value : (function() { return new Date(); })()
41563                         },
41564                         {
41565                             xtype: 'Separator',
41566                             xns: Roo.Toolbar
41567                         },
41568                         {
41569                             xtype: 'TextItem',
41570                             xns: Roo.Toolbar,
41571                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41572                         },
41573                         {
41574                             xtype: 'Fill',
41575                             xns: Roo.Toolbar
41576                         },
41577                         {
41578                             xtype: 'Button',
41579                             xns: Roo.Toolbar,
41580                             listeners : {
41581                                 click : function (_self, e)
41582                                 {
41583                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41584                                     sd.setMonth(sd.getMonth()+1);
41585                                     _this.monthField.setValue(sd.format('Y-m-d'));
41586                                     _this.grid.ds.load({});
41587                                 }
41588                             },
41589                             text : "Next"
41590                         }
41591                     ]
41592                 },
41593                  
41594             }
41595         };
41596         
41597         *//*
41598  * Based on:
41599  * Ext JS Library 1.1.1
41600  * Copyright(c) 2006-2007, Ext JS, LLC.
41601  *
41602  * Originally Released Under LGPL - original licence link has changed is not relivant.
41603  *
41604  * Fork - LGPL
41605  * <script type="text/javascript">
41606  */
41607  
41608 /**
41609  * @class Roo.LoadMask
41610  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41611  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41612  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41613  * element's UpdateManager load indicator and will be destroyed after the initial load.
41614  * @constructor
41615  * Create a new LoadMask
41616  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41617  * @param {Object} config The config object
41618  */
41619 Roo.LoadMask = function(el, config){
41620     this.el = Roo.get(el);
41621     Roo.apply(this, config);
41622     if(this.store){
41623         this.store.on('beforeload', this.onBeforeLoad, this);
41624         this.store.on('load', this.onLoad, this);
41625         this.store.on('loadexception', this.onLoadException, this);
41626         this.removeMask = false;
41627     }else{
41628         var um = this.el.getUpdateManager();
41629         um.showLoadIndicator = false; // disable the default indicator
41630         um.on('beforeupdate', this.onBeforeLoad, this);
41631         um.on('update', this.onLoad, this);
41632         um.on('failure', this.onLoad, this);
41633         this.removeMask = true;
41634     }
41635 };
41636
41637 Roo.LoadMask.prototype = {
41638     /**
41639      * @cfg {Boolean} removeMask
41640      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41641      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41642      */
41643     /**
41644      * @cfg {String} msg
41645      * The text to display in a centered loading message box (defaults to 'Loading...')
41646      */
41647     msg : 'Loading...',
41648     /**
41649      * @cfg {String} msgCls
41650      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41651      */
41652     msgCls : 'x-mask-loading',
41653
41654     /**
41655      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41656      * @type Boolean
41657      */
41658     disabled: false,
41659
41660     /**
41661      * Disables the mask to prevent it from being displayed
41662      */
41663     disable : function(){
41664        this.disabled = true;
41665     },
41666
41667     /**
41668      * Enables the mask so that it can be displayed
41669      */
41670     enable : function(){
41671         this.disabled = false;
41672     },
41673     
41674     onLoadException : function()
41675     {
41676         Roo.log(arguments);
41677         
41678         if (typeof(arguments[3]) != 'undefined') {
41679             Roo.MessageBox.alert("Error loading",arguments[3]);
41680         } 
41681         /*
41682         try {
41683             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41684                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41685             }   
41686         } catch(e) {
41687             
41688         }
41689         */
41690     
41691         
41692         
41693         this.el.unmask(this.removeMask);
41694     },
41695     // private
41696     onLoad : function()
41697     {
41698         this.el.unmask(this.removeMask);
41699     },
41700
41701     // private
41702     onBeforeLoad : function(){
41703         if(!this.disabled){
41704             this.el.mask(this.msg, this.msgCls);
41705         }
41706     },
41707
41708     // private
41709     destroy : function(){
41710         if(this.store){
41711             this.store.un('beforeload', this.onBeforeLoad, this);
41712             this.store.un('load', this.onLoad, this);
41713             this.store.un('loadexception', this.onLoadException, this);
41714         }else{
41715             var um = this.el.getUpdateManager();
41716             um.un('beforeupdate', this.onBeforeLoad, this);
41717             um.un('update', this.onLoad, this);
41718             um.un('failure', this.onLoad, this);
41719         }
41720     }
41721 };/*
41722  * Based on:
41723  * Ext JS Library 1.1.1
41724  * Copyright(c) 2006-2007, Ext JS, LLC.
41725  *
41726  * Originally Released Under LGPL - original licence link has changed is not relivant.
41727  *
41728  * Fork - LGPL
41729  * <script type="text/javascript">
41730  */
41731
41732
41733 /**
41734  * @class Roo.XTemplate
41735  * @extends Roo.Template
41736  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41737 <pre><code>
41738 var t = new Roo.XTemplate(
41739         '&lt;select name="{name}"&gt;',
41740                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41741         '&lt;/select&gt;'
41742 );
41743  
41744 // then append, applying the master template values
41745  </code></pre>
41746  *
41747  * Supported features:
41748  *
41749  *  Tags:
41750
41751 <pre><code>
41752       {a_variable} - output encoded.
41753       {a_variable.format:("Y-m-d")} - call a method on the variable
41754       {a_variable:raw} - unencoded output
41755       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41756       {a_variable:this.method_on_template(...)} - call a method on the template object.
41757  
41758 </code></pre>
41759  *  The tpl tag:
41760 <pre><code>
41761         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41762         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41763         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41764         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41765   
41766         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41767         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41768 </code></pre>
41769  *      
41770  */
41771 Roo.XTemplate = function()
41772 {
41773     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41774     if (this.html) {
41775         this.compile();
41776     }
41777 };
41778
41779
41780 Roo.extend(Roo.XTemplate, Roo.Template, {
41781
41782     /**
41783      * The various sub templates
41784      */
41785     tpls : false,
41786     /**
41787      *
41788      * basic tag replacing syntax
41789      * WORD:WORD()
41790      *
41791      * // you can fake an object call by doing this
41792      *  x.t:(test,tesT) 
41793      * 
41794      */
41795     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41796
41797     /**
41798      * compile the template
41799      *
41800      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41801      *
41802      */
41803     compile: function()
41804     {
41805         var s = this.html;
41806      
41807         s = ['<tpl>', s, '</tpl>'].join('');
41808     
41809         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41810             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41811             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41812             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41813             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41814             m,
41815             id     = 0,
41816             tpls   = [];
41817     
41818         while(true == !!(m = s.match(re))){
41819             var forMatch   = m[0].match(nameRe),
41820                 ifMatch   = m[0].match(ifRe),
41821                 execMatch   = m[0].match(execRe),
41822                 namedMatch   = m[0].match(namedRe),
41823                 
41824                 exp  = null, 
41825                 fn   = null,
41826                 exec = null,
41827                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41828                 
41829             if (ifMatch) {
41830                 // if - puts fn into test..
41831                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41832                 if(exp){
41833                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41834                 }
41835             }
41836             
41837             if (execMatch) {
41838                 // exec - calls a function... returns empty if true is  returned.
41839                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41840                 if(exp){
41841                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41842                 }
41843             }
41844             
41845             
41846             if (name) {
41847                 // for = 
41848                 switch(name){
41849                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41850                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41851                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41852                 }
41853             }
41854             var uid = namedMatch ? namedMatch[1] : id;
41855             
41856             
41857             tpls.push({
41858                 id:     namedMatch ? namedMatch[1] : id,
41859                 target: name,
41860                 exec:   exec,
41861                 test:   fn,
41862                 body:   m[1] || ''
41863             });
41864             if (namedMatch) {
41865                 s = s.replace(m[0], '');
41866             } else { 
41867                 s = s.replace(m[0], '{xtpl'+ id + '}');
41868             }
41869             ++id;
41870         }
41871         this.tpls = [];
41872         for(var i = tpls.length-1; i >= 0; --i){
41873             this.compileTpl(tpls[i]);
41874             this.tpls[tpls[i].id] = tpls[i];
41875         }
41876         this.master = tpls[tpls.length-1];
41877         return this;
41878     },
41879     /**
41880      * same as applyTemplate, except it's done to one of the subTemplates
41881      * when using named templates, you can do:
41882      *
41883      * var str = pl.applySubTemplate('your-name', values);
41884      *
41885      * 
41886      * @param {Number} id of the template
41887      * @param {Object} values to apply to template
41888      * @param {Object} parent (normaly the instance of this object)
41889      */
41890     applySubTemplate : function(id, values, parent)
41891     {
41892         
41893         
41894         var t = this.tpls[id];
41895         
41896         
41897         try { 
41898             if(t.test && !t.test.call(this, values, parent)){
41899                 return '';
41900             }
41901         } catch(e) {
41902             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41903             Roo.log(e.toString());
41904             Roo.log(t.test);
41905             return ''
41906         }
41907         try { 
41908             
41909             if(t.exec && t.exec.call(this, values, parent)){
41910                 return '';
41911             }
41912         } catch(e) {
41913             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41914             Roo.log(e.toString());
41915             Roo.log(t.exec);
41916             return ''
41917         }
41918         try {
41919             var vs = t.target ? t.target.call(this, values, parent) : values;
41920             parent = t.target ? values : parent;
41921             if(t.target && vs instanceof Array){
41922                 var buf = [];
41923                 for(var i = 0, len = vs.length; i < len; i++){
41924                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41925                 }
41926                 return buf.join('');
41927             }
41928             return t.compiled.call(this, vs, parent);
41929         } catch (e) {
41930             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41931             Roo.log(e.toString());
41932             Roo.log(t.compiled);
41933             return '';
41934         }
41935     },
41936
41937     compileTpl : function(tpl)
41938     {
41939         var fm = Roo.util.Format;
41940         var useF = this.disableFormats !== true;
41941         var sep = Roo.isGecko ? "+" : ",";
41942         var undef = function(str) {
41943             Roo.log("Property not found :"  + str);
41944             return '';
41945         };
41946         
41947         var fn = function(m, name, format, args)
41948         {
41949             //Roo.log(arguments);
41950             args = args ? args.replace(/\\'/g,"'") : args;
41951             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41952             if (typeof(format) == 'undefined') {
41953                 format= 'htmlEncode';
41954             }
41955             if (format == 'raw' ) {
41956                 format = false;
41957             }
41958             
41959             if(name.substr(0, 4) == 'xtpl'){
41960                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41961             }
41962             
41963             // build an array of options to determine if value is undefined..
41964             
41965             // basically get 'xxxx.yyyy' then do
41966             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41967             //    (function () { Roo.log("Property not found"); return ''; })() :
41968             //    ......
41969             
41970             var udef_ar = [];
41971             var lookfor = '';
41972             Roo.each(name.split('.'), function(st) {
41973                 lookfor += (lookfor.length ? '.': '') + st;
41974                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41975             });
41976             
41977             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41978             
41979             
41980             if(format && useF){
41981                 
41982                 args = args ? ',' + args : "";
41983                  
41984                 if(format.substr(0, 5) != "this."){
41985                     format = "fm." + format + '(';
41986                 }else{
41987                     format = 'this.call("'+ format.substr(5) + '", ';
41988                     args = ", values";
41989                 }
41990                 
41991                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41992             }
41993              
41994             if (args.length) {
41995                 // called with xxyx.yuu:(test,test)
41996                 // change to ()
41997                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41998             }
41999             // raw.. - :raw modifier..
42000             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42001             
42002         };
42003         var body;
42004         // branched to use + in gecko and [].join() in others
42005         if(Roo.isGecko){
42006             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42007                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42008                     "';};};";
42009         }else{
42010             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42011             body.push(tpl.body.replace(/(\r\n|\n)/g,
42012                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42013             body.push("'].join('');};};");
42014             body = body.join('');
42015         }
42016         
42017         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42018        
42019         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42020         eval(body);
42021         
42022         return this;
42023     },
42024
42025     applyTemplate : function(values){
42026         return this.master.compiled.call(this, values, {});
42027         //var s = this.subs;
42028     },
42029
42030     apply : function(){
42031         return this.applyTemplate.apply(this, arguments);
42032     }
42033
42034  });
42035
42036 Roo.XTemplate.from = function(el){
42037     el = Roo.getDom(el);
42038     return new Roo.XTemplate(el.value || el.innerHTML);
42039 };