53ee3d824f1da4371f717dcfeba098fa4bbee01c
[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     {
11434         if(e){
11435             e.preventDefault();
11436         }
11437         if(e.button != 0){
11438             return;
11439         }
11440         if(!this.disabled){
11441             if(this.enableToggle){
11442                 this.toggle();
11443             }
11444             if(this.menu && !this.menu.isVisible()){
11445                 this.menu.show(this.el, this.menuAlign);
11446             }
11447             this.fireEvent("click", this, e);
11448             if(this.handler){
11449                 this.el.removeClass("x-btn-over");
11450                 this.handler.call(this.scope || this, this, e);
11451             }
11452         }
11453     },
11454     // private
11455     onMouseOver : function(e){
11456         if(!this.disabled){
11457             this.el.addClass("x-btn-over");
11458             this.fireEvent('mouseover', this, e);
11459         }
11460     },
11461     // private
11462     onMouseOut : function(e){
11463         if(!e.within(this.el,  true)){
11464             this.el.removeClass("x-btn-over");
11465             this.fireEvent('mouseout', this, e);
11466         }
11467     },
11468     // private
11469     onFocus : function(e){
11470         if(!this.disabled){
11471             this.el.addClass("x-btn-focus");
11472         }
11473     },
11474     // private
11475     onBlur : function(e){
11476         this.el.removeClass("x-btn-focus");
11477     },
11478     // private
11479     onMouseDown : function(e){
11480         if(!this.disabled && e.button == 0){
11481             this.el.addClass("x-btn-click");
11482             Roo.get(document).on('mouseup', this.onMouseUp, this);
11483         }
11484     },
11485     // private
11486     onMouseUp : function(e){
11487         if(e.button == 0){
11488             this.el.removeClass("x-btn-click");
11489             Roo.get(document).un('mouseup', this.onMouseUp, this);
11490         }
11491     },
11492     // private
11493     onMenuShow : function(e){
11494         this.el.addClass("x-btn-menu-active");
11495     },
11496     // private
11497     onMenuHide : function(e){
11498         this.el.removeClass("x-btn-menu-active");
11499     }   
11500 });
11501
11502 // Private utility class used by Button
11503 Roo.ButtonToggleMgr = function(){
11504    var groups = {};
11505    
11506    function toggleGroup(btn, state){
11507        if(state){
11508            var g = groups[btn.toggleGroup];
11509            for(var i = 0, l = g.length; i < l; i++){
11510                if(g[i] != btn){
11511                    g[i].toggle(false);
11512                }
11513            }
11514        }
11515    }
11516    
11517    return {
11518        register : function(btn){
11519            if(!btn.toggleGroup){
11520                return;
11521            }
11522            var g = groups[btn.toggleGroup];
11523            if(!g){
11524                g = groups[btn.toggleGroup] = [];
11525            }
11526            g.push(btn);
11527            btn.on("toggle", toggleGroup);
11528        },
11529        
11530        unregister : function(btn){
11531            if(!btn.toggleGroup){
11532                return;
11533            }
11534            var g = groups[btn.toggleGroup];
11535            if(g){
11536                g.remove(btn);
11537                btn.un("toggle", toggleGroup);
11538            }
11539        }
11540    };
11541 }();/*
11542  * Based on:
11543  * Ext JS Library 1.1.1
11544  * Copyright(c) 2006-2007, Ext JS, LLC.
11545  *
11546  * Originally Released Under LGPL - original licence link has changed is not relivant.
11547  *
11548  * Fork - LGPL
11549  * <script type="text/javascript">
11550  */
11551  
11552 /**
11553  * @class Roo.SplitButton
11554  * @extends Roo.Button
11555  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11556  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11557  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11558  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11559  * @cfg {String} arrowTooltip The title attribute of the arrow
11560  * @constructor
11561  * Create a new menu button
11562  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11563  * @param {Object} config The config object
11564  */
11565 Roo.SplitButton = function(renderTo, config){
11566     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11567     /**
11568      * @event arrowclick
11569      * Fires when this button's arrow is clicked
11570      * @param {SplitButton} this
11571      * @param {EventObject} e The click event
11572      */
11573     this.addEvents({"arrowclick":true});
11574 };
11575
11576 Roo.extend(Roo.SplitButton, Roo.Button, {
11577     render : function(renderTo){
11578         // this is one sweet looking template!
11579         var tpl = new Roo.Template(
11580             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11581             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11582             '<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>',
11583             "</tbody></table></td><td>",
11584             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11585             '<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>',
11586             "</tbody></table></td></tr></table>"
11587         );
11588         var btn = tpl.append(renderTo, [this.text, this.type], true);
11589         var btnEl = btn.child("button");
11590         if(this.cls){
11591             btn.addClass(this.cls);
11592         }
11593         if(this.icon){
11594             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11595         }
11596         if(this.iconCls){
11597             btnEl.addClass(this.iconCls);
11598             if(!this.cls){
11599                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11600             }
11601         }
11602         this.el = btn;
11603         if(this.handleMouseEvents){
11604             btn.on("mouseover", this.onMouseOver, this);
11605             btn.on("mouseout", this.onMouseOut, this);
11606             btn.on("mousedown", this.onMouseDown, this);
11607             btn.on("mouseup", this.onMouseUp, this);
11608         }
11609         btn.on(this.clickEvent, this.onClick, this);
11610         if(this.tooltip){
11611             if(typeof this.tooltip == 'object'){
11612                 Roo.QuickTips.tips(Roo.apply({
11613                       target: btnEl.id
11614                 }, this.tooltip));
11615             } else {
11616                 btnEl.dom[this.tooltipType] = this.tooltip;
11617             }
11618         }
11619         if(this.arrowTooltip){
11620             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11621         }
11622         if(this.hidden){
11623             this.hide();
11624         }
11625         if(this.disabled){
11626             this.disable();
11627         }
11628         if(this.pressed){
11629             this.el.addClass("x-btn-pressed");
11630         }
11631         if(Roo.isIE && !Roo.isIE7){
11632             this.autoWidth.defer(1, this);
11633         }else{
11634             this.autoWidth();
11635         }
11636         if(this.menu){
11637             this.menu.on("show", this.onMenuShow, this);
11638             this.menu.on("hide", this.onMenuHide, this);
11639         }
11640         this.fireEvent('render', this);
11641     },
11642
11643     // private
11644     autoWidth : function(){
11645         if(this.el){
11646             var tbl = this.el.child("table:first");
11647             var tbl2 = this.el.child("table:last");
11648             this.el.setWidth("auto");
11649             tbl.setWidth("auto");
11650             if(Roo.isIE7 && Roo.isStrict){
11651                 var ib = this.el.child('button:first');
11652                 if(ib && ib.getWidth() > 20){
11653                     ib.clip();
11654                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11655                 }
11656             }
11657             if(this.minWidth){
11658                 if(this.hidden){
11659                     this.el.beginMeasure();
11660                 }
11661                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11662                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11663                 }
11664                 if(this.hidden){
11665                     this.el.endMeasure();
11666                 }
11667             }
11668             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11669         } 
11670     },
11671     /**
11672      * Sets this button's click handler
11673      * @param {Function} handler The function to call when the button is clicked
11674      * @param {Object} scope (optional) Scope for the function passed above
11675      */
11676     setHandler : function(handler, scope){
11677         this.handler = handler;
11678         this.scope = scope;  
11679     },
11680     
11681     /**
11682      * Sets this button's arrow click handler
11683      * @param {Function} handler The function to call when the arrow is clicked
11684      * @param {Object} scope (optional) Scope for the function passed above
11685      */
11686     setArrowHandler : function(handler, scope){
11687         this.arrowHandler = handler;
11688         this.scope = scope;  
11689     },
11690     
11691     /**
11692      * Focus the button
11693      */
11694     focus : function(){
11695         if(this.el){
11696             this.el.child("button:first").focus();
11697         }
11698     },
11699
11700     // private
11701     onClick : function(e){
11702         e.preventDefault();
11703         if(!this.disabled){
11704             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11705                 if(this.menu && !this.menu.isVisible()){
11706                     this.menu.show(this.el, this.menuAlign);
11707                 }
11708                 this.fireEvent("arrowclick", this, e);
11709                 if(this.arrowHandler){
11710                     this.arrowHandler.call(this.scope || this, this, e);
11711                 }
11712             }else{
11713                 this.fireEvent("click", this, e);
11714                 if(this.handler){
11715                     this.handler.call(this.scope || this, this, e);
11716                 }
11717             }
11718         }
11719     },
11720     // private
11721     onMouseDown : function(e){
11722         if(!this.disabled){
11723             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11724         }
11725     },
11726     // private
11727     onMouseUp : function(e){
11728         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11729     }   
11730 });
11731
11732
11733 // backwards compat
11734 Roo.MenuButton = Roo.SplitButton;/*
11735  * Based on:
11736  * Ext JS Library 1.1.1
11737  * Copyright(c) 2006-2007, Ext JS, LLC.
11738  *
11739  * Originally Released Under LGPL - original licence link has changed is not relivant.
11740  *
11741  * Fork - LGPL
11742  * <script type="text/javascript">
11743  */
11744
11745 /**
11746  * @class Roo.Toolbar
11747  * Basic Toolbar class.
11748  * @constructor
11749  * Creates a new Toolbar
11750  * @param {Object} container The config object
11751  */ 
11752 Roo.Toolbar = function(container, buttons, config)
11753 {
11754     /// old consturctor format still supported..
11755     if(container instanceof Array){ // omit the container for later rendering
11756         buttons = container;
11757         config = buttons;
11758         container = null;
11759     }
11760     if (typeof(container) == 'object' && container.xtype) {
11761         config = container;
11762         container = config.container;
11763         buttons = config.buttons || []; // not really - use items!!
11764     }
11765     var xitems = [];
11766     if (config && config.items) {
11767         xitems = config.items;
11768         delete config.items;
11769     }
11770     Roo.apply(this, config);
11771     this.buttons = buttons;
11772     
11773     if(container){
11774         this.render(container);
11775     }
11776     this.xitems = xitems;
11777     Roo.each(xitems, function(b) {
11778         this.add(b);
11779     }, this);
11780     
11781 };
11782
11783 Roo.Toolbar.prototype = {
11784     /**
11785      * @cfg {Array} items
11786      * array of button configs or elements to add (will be converted to a MixedCollection)
11787      */
11788     
11789     /**
11790      * @cfg {String/HTMLElement/Element} container
11791      * The id or element that will contain the toolbar
11792      */
11793     // private
11794     render : function(ct){
11795         this.el = Roo.get(ct);
11796         if(this.cls){
11797             this.el.addClass(this.cls);
11798         }
11799         // using a table allows for vertical alignment
11800         // 100% width is needed by Safari...
11801         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11802         this.tr = this.el.child("tr", true);
11803         var autoId = 0;
11804         this.items = new Roo.util.MixedCollection(false, function(o){
11805             return o.id || ("item" + (++autoId));
11806         });
11807         if(this.buttons){
11808             this.add.apply(this, this.buttons);
11809             delete this.buttons;
11810         }
11811     },
11812
11813     /**
11814      * Adds element(s) to the toolbar -- this function takes a variable number of 
11815      * arguments of mixed type and adds them to the toolbar.
11816      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11817      * <ul>
11818      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11819      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11820      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11821      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11822      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11823      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11824      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11825      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11826      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11827      * </ul>
11828      * @param {Mixed} arg2
11829      * @param {Mixed} etc.
11830      */
11831     add : function(){
11832         var a = arguments, l = a.length;
11833         for(var i = 0; i < l; i++){
11834             this._add(a[i]);
11835         }
11836     },
11837     // private..
11838     _add : function(el) {
11839         
11840         if (el.xtype) {
11841             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11842         }
11843         
11844         if (el.applyTo){ // some kind of form field
11845             return this.addField(el);
11846         } 
11847         if (el.render){ // some kind of Toolbar.Item
11848             return this.addItem(el);
11849         }
11850         if (typeof el == "string"){ // string
11851             if(el == "separator" || el == "-"){
11852                 return this.addSeparator();
11853             }
11854             if (el == " "){
11855                 return this.addSpacer();
11856             }
11857             if(el == "->"){
11858                 return this.addFill();
11859             }
11860             return this.addText(el);
11861             
11862         }
11863         if(el.tagName){ // element
11864             return this.addElement(el);
11865         }
11866         if(typeof el == "object"){ // must be button config?
11867             return this.addButton(el);
11868         }
11869         // and now what?!?!
11870         return false;
11871         
11872     },
11873     
11874     /**
11875      * Add an Xtype element
11876      * @param {Object} xtype Xtype Object
11877      * @return {Object} created Object
11878      */
11879     addxtype : function(e){
11880         return this.add(e);  
11881     },
11882     
11883     /**
11884      * Returns the Element for this toolbar.
11885      * @return {Roo.Element}
11886      */
11887     getEl : function(){
11888         return this.el;  
11889     },
11890     
11891     /**
11892      * Adds a separator
11893      * @return {Roo.Toolbar.Item} The separator item
11894      */
11895     addSeparator : function(){
11896         return this.addItem(new Roo.Toolbar.Separator());
11897     },
11898
11899     /**
11900      * Adds a spacer element
11901      * @return {Roo.Toolbar.Spacer} The spacer item
11902      */
11903     addSpacer : function(){
11904         return this.addItem(new Roo.Toolbar.Spacer());
11905     },
11906
11907     /**
11908      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11909      * @return {Roo.Toolbar.Fill} The fill item
11910      */
11911     addFill : function(){
11912         return this.addItem(new Roo.Toolbar.Fill());
11913     },
11914
11915     /**
11916      * Adds any standard HTML element to the toolbar
11917      * @param {String/HTMLElement/Element} el The element or id of the element to add
11918      * @return {Roo.Toolbar.Item} The element's item
11919      */
11920     addElement : function(el){
11921         return this.addItem(new Roo.Toolbar.Item(el));
11922     },
11923     /**
11924      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11925      * @type Roo.util.MixedCollection  
11926      */
11927     items : false,
11928      
11929     /**
11930      * Adds any Toolbar.Item or subclass
11931      * @param {Roo.Toolbar.Item} item
11932      * @return {Roo.Toolbar.Item} The item
11933      */
11934     addItem : function(item){
11935         var td = this.nextBlock();
11936         item.render(td);
11937         this.items.add(item);
11938         return item;
11939     },
11940     
11941     /**
11942      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11943      * @param {Object/Array} config A button config or array of configs
11944      * @return {Roo.Toolbar.Button/Array}
11945      */
11946     addButton : function(config){
11947         if(config instanceof Array){
11948             var buttons = [];
11949             for(var i = 0, len = config.length; i < len; i++) {
11950                 buttons.push(this.addButton(config[i]));
11951             }
11952             return buttons;
11953         }
11954         var b = config;
11955         if(!(config instanceof Roo.Toolbar.Button)){
11956             b = config.split ?
11957                 new Roo.Toolbar.SplitButton(config) :
11958                 new Roo.Toolbar.Button(config);
11959         }
11960         var td = this.nextBlock();
11961         b.render(td);
11962         this.items.add(b);
11963         return b;
11964     },
11965     
11966     /**
11967      * Adds text to the toolbar
11968      * @param {String} text The text to add
11969      * @return {Roo.Toolbar.Item} The element's item
11970      */
11971     addText : function(text){
11972         return this.addItem(new Roo.Toolbar.TextItem(text));
11973     },
11974     
11975     /**
11976      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11977      * @param {Number} index The index where the item is to be inserted
11978      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11979      * @return {Roo.Toolbar.Button/Item}
11980      */
11981     insertButton : function(index, item){
11982         if(item instanceof Array){
11983             var buttons = [];
11984             for(var i = 0, len = item.length; i < len; i++) {
11985                buttons.push(this.insertButton(index + i, item[i]));
11986             }
11987             return buttons;
11988         }
11989         if (!(item instanceof Roo.Toolbar.Button)){
11990            item = new Roo.Toolbar.Button(item);
11991         }
11992         var td = document.createElement("td");
11993         this.tr.insertBefore(td, this.tr.childNodes[index]);
11994         item.render(td);
11995         this.items.insert(index, item);
11996         return item;
11997     },
11998     
11999     /**
12000      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12001      * @param {Object} config
12002      * @return {Roo.Toolbar.Item} The element's item
12003      */
12004     addDom : function(config, returnEl){
12005         var td = this.nextBlock();
12006         Roo.DomHelper.overwrite(td, config);
12007         var ti = new Roo.Toolbar.Item(td.firstChild);
12008         ti.render(td);
12009         this.items.add(ti);
12010         return ti;
12011     },
12012
12013     /**
12014      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12015      * @type Roo.util.MixedCollection  
12016      */
12017     fields : false,
12018     
12019     /**
12020      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12021      * Note: the field should not have been rendered yet. For a field that has already been
12022      * rendered, use {@link #addElement}.
12023      * @param {Roo.form.Field} field
12024      * @return {Roo.ToolbarItem}
12025      */
12026      
12027       
12028     addField : function(field) {
12029         if (!this.fields) {
12030             var autoId = 0;
12031             this.fields = new Roo.util.MixedCollection(false, function(o){
12032                 return o.id || ("item" + (++autoId));
12033             });
12034
12035         }
12036         
12037         var td = this.nextBlock();
12038         field.render(td);
12039         var ti = new Roo.Toolbar.Item(td.firstChild);
12040         ti.render(td);
12041         this.items.add(ti);
12042         this.fields.add(field);
12043         return ti;
12044     },
12045     /**
12046      * Hide the toolbar
12047      * @method hide
12048      */
12049      
12050       
12051     hide : function()
12052     {
12053         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12054         this.el.child('div').hide();
12055     },
12056     /**
12057      * Show the toolbar
12058      * @method show
12059      */
12060     show : function()
12061     {
12062         this.el.child('div').show();
12063     },
12064       
12065     // private
12066     nextBlock : function(){
12067         var td = document.createElement("td");
12068         this.tr.appendChild(td);
12069         return td;
12070     },
12071
12072     // private
12073     destroy : function(){
12074         if(this.items){ // rendered?
12075             Roo.destroy.apply(Roo, this.items.items);
12076         }
12077         if(this.fields){ // rendered?
12078             Roo.destroy.apply(Roo, this.fields.items);
12079         }
12080         Roo.Element.uncache(this.el, this.tr);
12081     }
12082 };
12083
12084 /**
12085  * @class Roo.Toolbar.Item
12086  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12087  * @constructor
12088  * Creates a new Item
12089  * @param {HTMLElement} el 
12090  */
12091 Roo.Toolbar.Item = function(el){
12092     var cfg = {};
12093     if (typeof (el.xtype) != 'undefined') {
12094         cfg = el;
12095         el = cfg.el;
12096     }
12097     
12098     this.el = Roo.getDom(el);
12099     this.id = Roo.id(this.el);
12100     this.hidden = false;
12101     
12102     this.addEvents({
12103          /**
12104              * @event render
12105              * Fires when the button is rendered
12106              * @param {Button} this
12107              */
12108         'render': true
12109     });
12110     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12111 };
12112 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12113 //Roo.Toolbar.Item.prototype = {
12114     
12115     /**
12116      * Get this item's HTML Element
12117      * @return {HTMLElement}
12118      */
12119     getEl : function(){
12120        return this.el;  
12121     },
12122
12123     // private
12124     render : function(td){
12125         
12126          this.td = td;
12127         td.appendChild(this.el);
12128         
12129         this.fireEvent('render', this);
12130     },
12131     
12132     /**
12133      * Removes and destroys this item.
12134      */
12135     destroy : function(){
12136         this.td.parentNode.removeChild(this.td);
12137     },
12138     
12139     /**
12140      * Shows this item.
12141      */
12142     show: function(){
12143         this.hidden = false;
12144         this.td.style.display = "";
12145     },
12146     
12147     /**
12148      * Hides this item.
12149      */
12150     hide: function(){
12151         this.hidden = true;
12152         this.td.style.display = "none";
12153     },
12154     
12155     /**
12156      * Convenience function for boolean show/hide.
12157      * @param {Boolean} visible true to show/false to hide
12158      */
12159     setVisible: function(visible){
12160         if(visible) {
12161             this.show();
12162         }else{
12163             this.hide();
12164         }
12165     },
12166     
12167     /**
12168      * Try to focus this item.
12169      */
12170     focus : function(){
12171         Roo.fly(this.el).focus();
12172     },
12173     
12174     /**
12175      * Disables this item.
12176      */
12177     disable : function(){
12178         Roo.fly(this.td).addClass("x-item-disabled");
12179         this.disabled = true;
12180         this.el.disabled = true;
12181     },
12182     
12183     /**
12184      * Enables this item.
12185      */
12186     enable : function(){
12187         Roo.fly(this.td).removeClass("x-item-disabled");
12188         this.disabled = false;
12189         this.el.disabled = false;
12190     }
12191 });
12192
12193
12194 /**
12195  * @class Roo.Toolbar.Separator
12196  * @extends Roo.Toolbar.Item
12197  * A simple toolbar separator class
12198  * @constructor
12199  * Creates a new Separator
12200  */
12201 Roo.Toolbar.Separator = function(cfg){
12202     
12203     var s = document.createElement("span");
12204     s.className = "ytb-sep";
12205     if (cfg) {
12206         cfg.el = s;
12207     }
12208     
12209     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12210 };
12211 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12212     enable:Roo.emptyFn,
12213     disable:Roo.emptyFn,
12214     focus:Roo.emptyFn
12215 });
12216
12217 /**
12218  * @class Roo.Toolbar.Spacer
12219  * @extends Roo.Toolbar.Item
12220  * A simple element that adds extra horizontal space to a toolbar.
12221  * @constructor
12222  * Creates a new Spacer
12223  */
12224 Roo.Toolbar.Spacer = function(cfg){
12225     var s = document.createElement("div");
12226     s.className = "ytb-spacer";
12227     if (cfg) {
12228         cfg.el = s;
12229     }
12230     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12231 };
12232 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12233     enable:Roo.emptyFn,
12234     disable:Roo.emptyFn,
12235     focus:Roo.emptyFn
12236 });
12237
12238 /**
12239  * @class Roo.Toolbar.Fill
12240  * @extends Roo.Toolbar.Spacer
12241  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12242  * @constructor
12243  * Creates a new Spacer
12244  */
12245 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12246     // private
12247     render : function(td){
12248         td.style.width = '100%';
12249         Roo.Toolbar.Fill.superclass.render.call(this, td);
12250     }
12251 });
12252
12253 /**
12254  * @class Roo.Toolbar.TextItem
12255  * @extends Roo.Toolbar.Item
12256  * A simple class that renders text directly into a toolbar.
12257  * @constructor
12258  * Creates a new TextItem
12259  * @param {String} text
12260  */
12261 Roo.Toolbar.TextItem = function(cfg){
12262     var  text = cfg || "";
12263     if (typeof(cfg) == 'object') {
12264         text = cfg.text || "";
12265     }  else {
12266         cfg = null;
12267     }
12268     var s = document.createElement("span");
12269     s.className = "ytb-text";
12270     s.innerHTML = text;
12271     if (cfg) {
12272         cfg.el  = s;
12273     }
12274     
12275     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12276 };
12277 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12278     
12279      
12280     enable:Roo.emptyFn,
12281     disable:Roo.emptyFn,
12282     focus:Roo.emptyFn
12283 });
12284
12285 /**
12286  * @class Roo.Toolbar.Button
12287  * @extends Roo.Button
12288  * A button that renders into a toolbar.
12289  * @constructor
12290  * Creates a new Button
12291  * @param {Object} config A standard {@link Roo.Button} config object
12292  */
12293 Roo.Toolbar.Button = function(config){
12294     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12295 };
12296 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12297     render : function(td){
12298         this.td = td;
12299         Roo.Toolbar.Button.superclass.render.call(this, td);
12300     },
12301     
12302     /**
12303      * Removes and destroys this button
12304      */
12305     destroy : function(){
12306         Roo.Toolbar.Button.superclass.destroy.call(this);
12307         this.td.parentNode.removeChild(this.td);
12308     },
12309     
12310     /**
12311      * Shows this button
12312      */
12313     show: function(){
12314         this.hidden = false;
12315         this.td.style.display = "";
12316     },
12317     
12318     /**
12319      * Hides this button
12320      */
12321     hide: function(){
12322         this.hidden = true;
12323         this.td.style.display = "none";
12324     },
12325
12326     /**
12327      * Disables this item
12328      */
12329     disable : function(){
12330         Roo.fly(this.td).addClass("x-item-disabled");
12331         this.disabled = true;
12332     },
12333
12334     /**
12335      * Enables this item
12336      */
12337     enable : function(){
12338         Roo.fly(this.td).removeClass("x-item-disabled");
12339         this.disabled = false;
12340     }
12341 });
12342 // backwards compat
12343 Roo.ToolbarButton = Roo.Toolbar.Button;
12344
12345 /**
12346  * @class Roo.Toolbar.SplitButton
12347  * @extends Roo.SplitButton
12348  * A menu button that renders into a toolbar.
12349  * @constructor
12350  * Creates a new SplitButton
12351  * @param {Object} config A standard {@link Roo.SplitButton} config object
12352  */
12353 Roo.Toolbar.SplitButton = function(config){
12354     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12355 };
12356 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12357     render : function(td){
12358         this.td = td;
12359         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12360     },
12361     
12362     /**
12363      * Removes and destroys this button
12364      */
12365     destroy : function(){
12366         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12367         this.td.parentNode.removeChild(this.td);
12368     },
12369     
12370     /**
12371      * Shows this button
12372      */
12373     show: function(){
12374         this.hidden = false;
12375         this.td.style.display = "";
12376     },
12377     
12378     /**
12379      * Hides this button
12380      */
12381     hide: function(){
12382         this.hidden = true;
12383         this.td.style.display = "none";
12384     }
12385 });
12386
12387 // backwards compat
12388 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12389  * Based on:
12390  * Ext JS Library 1.1.1
12391  * Copyright(c) 2006-2007, Ext JS, LLC.
12392  *
12393  * Originally Released Under LGPL - original licence link has changed is not relivant.
12394  *
12395  * Fork - LGPL
12396  * <script type="text/javascript">
12397  */
12398  
12399 /**
12400  * @class Roo.PagingToolbar
12401  * @extends Roo.Toolbar
12402  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12403  * @constructor
12404  * Create a new PagingToolbar
12405  * @param {Object} config The config object
12406  */
12407 Roo.PagingToolbar = function(el, ds, config)
12408 {
12409     // old args format still supported... - xtype is prefered..
12410     if (typeof(el) == 'object' && el.xtype) {
12411         // created from xtype...
12412         config = el;
12413         ds = el.dataSource;
12414         el = config.container;
12415     }
12416     var items = [];
12417     if (config.items) {
12418         items = config.items;
12419         config.items = [];
12420     }
12421     
12422     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12423     this.ds = ds;
12424     this.cursor = 0;
12425     this.renderButtons(this.el);
12426     this.bind(ds);
12427     
12428     // supprot items array.
12429    
12430     Roo.each(items, function(e) {
12431         this.add(Roo.factory(e));
12432     },this);
12433     
12434 };
12435
12436 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12437     /**
12438      * @cfg {Roo.data.Store} dataSource
12439      * The underlying data store providing the paged data
12440      */
12441     /**
12442      * @cfg {String/HTMLElement/Element} container
12443      * container The id or element that will contain the toolbar
12444      */
12445     /**
12446      * @cfg {Boolean} displayInfo
12447      * True to display the displayMsg (defaults to false)
12448      */
12449     /**
12450      * @cfg {Number} pageSize
12451      * The number of records to display per page (defaults to 20)
12452      */
12453     pageSize: 20,
12454     /**
12455      * @cfg {String} displayMsg
12456      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12457      */
12458     displayMsg : 'Displaying {0} - {1} of {2}',
12459     /**
12460      * @cfg {String} emptyMsg
12461      * The message to display when no records are found (defaults to "No data to display")
12462      */
12463     emptyMsg : 'No data to display',
12464     /**
12465      * Customizable piece of the default paging text (defaults to "Page")
12466      * @type String
12467      */
12468     beforePageText : "Page",
12469     /**
12470      * Customizable piece of the default paging text (defaults to "of %0")
12471      * @type String
12472      */
12473     afterPageText : "of {0}",
12474     /**
12475      * Customizable piece of the default paging text (defaults to "First Page")
12476      * @type String
12477      */
12478     firstText : "First Page",
12479     /**
12480      * Customizable piece of the default paging text (defaults to "Previous Page")
12481      * @type String
12482      */
12483     prevText : "Previous Page",
12484     /**
12485      * Customizable piece of the default paging text (defaults to "Next Page")
12486      * @type String
12487      */
12488     nextText : "Next Page",
12489     /**
12490      * Customizable piece of the default paging text (defaults to "Last Page")
12491      * @type String
12492      */
12493     lastText : "Last Page",
12494     /**
12495      * Customizable piece of the default paging text (defaults to "Refresh")
12496      * @type String
12497      */
12498     refreshText : "Refresh",
12499
12500     // private
12501     renderButtons : function(el){
12502         Roo.PagingToolbar.superclass.render.call(this, el);
12503         this.first = this.addButton({
12504             tooltip: this.firstText,
12505             cls: "x-btn-icon x-grid-page-first",
12506             disabled: true,
12507             handler: this.onClick.createDelegate(this, ["first"])
12508         });
12509         this.prev = this.addButton({
12510             tooltip: this.prevText,
12511             cls: "x-btn-icon x-grid-page-prev",
12512             disabled: true,
12513             handler: this.onClick.createDelegate(this, ["prev"])
12514         });
12515         //this.addSeparator();
12516         this.add(this.beforePageText);
12517         this.field = Roo.get(this.addDom({
12518            tag: "input",
12519            type: "text",
12520            size: "3",
12521            value: "1",
12522            cls: "x-grid-page-number"
12523         }).el);
12524         this.field.on("keydown", this.onPagingKeydown, this);
12525         this.field.on("focus", function(){this.dom.select();});
12526         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12527         this.field.setHeight(18);
12528         //this.addSeparator();
12529         this.next = this.addButton({
12530             tooltip: this.nextText,
12531             cls: "x-btn-icon x-grid-page-next",
12532             disabled: true,
12533             handler: this.onClick.createDelegate(this, ["next"])
12534         });
12535         this.last = this.addButton({
12536             tooltip: this.lastText,
12537             cls: "x-btn-icon x-grid-page-last",
12538             disabled: true,
12539             handler: this.onClick.createDelegate(this, ["last"])
12540         });
12541         //this.addSeparator();
12542         this.loading = this.addButton({
12543             tooltip: this.refreshText,
12544             cls: "x-btn-icon x-grid-loading",
12545             handler: this.onClick.createDelegate(this, ["refresh"])
12546         });
12547
12548         if(this.displayInfo){
12549             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12550         }
12551     },
12552
12553     // private
12554     updateInfo : function(){
12555         if(this.displayEl){
12556             var count = this.ds.getCount();
12557             var msg = count == 0 ?
12558                 this.emptyMsg :
12559                 String.format(
12560                     this.displayMsg,
12561                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12562                 );
12563             this.displayEl.update(msg);
12564         }
12565     },
12566
12567     // private
12568     onLoad : function(ds, r, o){
12569        this.cursor = o.params ? o.params.start : 0;
12570        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12571
12572        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12573        this.field.dom.value = ap;
12574        this.first.setDisabled(ap == 1);
12575        this.prev.setDisabled(ap == 1);
12576        this.next.setDisabled(ap == ps);
12577        this.last.setDisabled(ap == ps);
12578        this.loading.enable();
12579        this.updateInfo();
12580     },
12581
12582     // private
12583     getPageData : function(){
12584         var total = this.ds.getTotalCount();
12585         return {
12586             total : total,
12587             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12588             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12589         };
12590     },
12591
12592     // private
12593     onLoadError : function(){
12594         this.loading.enable();
12595     },
12596
12597     // private
12598     onPagingKeydown : function(e){
12599         var k = e.getKey();
12600         var d = this.getPageData();
12601         if(k == e.RETURN){
12602             var v = this.field.dom.value, pageNum;
12603             if(!v || isNaN(pageNum = parseInt(v, 10))){
12604                 this.field.dom.value = d.activePage;
12605                 return;
12606             }
12607             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12608             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12609             e.stopEvent();
12610         }
12611         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))
12612         {
12613           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12614           this.field.dom.value = pageNum;
12615           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12616           e.stopEvent();
12617         }
12618         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12619         {
12620           var v = this.field.dom.value, pageNum; 
12621           var increment = (e.shiftKey) ? 10 : 1;
12622           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12623             increment *= -1;
12624           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12625             this.field.dom.value = d.activePage;
12626             return;
12627           }
12628           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12629           {
12630             this.field.dom.value = parseInt(v, 10) + increment;
12631             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12632             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12633           }
12634           e.stopEvent();
12635         }
12636     },
12637
12638     // private
12639     beforeLoad : function(){
12640         if(this.loading){
12641             this.loading.disable();
12642         }
12643     },
12644
12645     // private
12646     onClick : function(which){
12647         var ds = this.ds;
12648         switch(which){
12649             case "first":
12650                 ds.load({params:{start: 0, limit: this.pageSize}});
12651             break;
12652             case "prev":
12653                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12654             break;
12655             case "next":
12656                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12657             break;
12658             case "last":
12659                 var total = ds.getTotalCount();
12660                 var extra = total % this.pageSize;
12661                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12662                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12663             break;
12664             case "refresh":
12665                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12666             break;
12667         }
12668     },
12669
12670     /**
12671      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12672      * @param {Roo.data.Store} store The data store to unbind
12673      */
12674     unbind : function(ds){
12675         ds.un("beforeload", this.beforeLoad, this);
12676         ds.un("load", this.onLoad, this);
12677         ds.un("loadexception", this.onLoadError, this);
12678         ds.un("remove", this.updateInfo, this);
12679         ds.un("add", this.updateInfo, this);
12680         this.ds = undefined;
12681     },
12682
12683     /**
12684      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12685      * @param {Roo.data.Store} store The data store to bind
12686      */
12687     bind : function(ds){
12688         ds.on("beforeload", this.beforeLoad, this);
12689         ds.on("load", this.onLoad, this);
12690         ds.on("loadexception", this.onLoadError, this);
12691         ds.on("remove", this.updateInfo, this);
12692         ds.on("add", this.updateInfo, this);
12693         this.ds = ds;
12694     }
12695 });/*
12696  * Based on:
12697  * Ext JS Library 1.1.1
12698  * Copyright(c) 2006-2007, Ext JS, LLC.
12699  *
12700  * Originally Released Under LGPL - original licence link has changed is not relivant.
12701  *
12702  * Fork - LGPL
12703  * <script type="text/javascript">
12704  */
12705
12706 /**
12707  * @class Roo.Resizable
12708  * @extends Roo.util.Observable
12709  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12710  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12711  * 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
12712  * the element will be wrapped for you automatically.</p>
12713  * <p>Here is the list of valid resize handles:</p>
12714  * <pre>
12715 Value   Description
12716 ------  -------------------
12717  'n'     north
12718  's'     south
12719  'e'     east
12720  'w'     west
12721  'nw'    northwest
12722  'sw'    southwest
12723  'se'    southeast
12724  'ne'    northeast
12725  'hd'    horizontal drag
12726  'all'   all
12727 </pre>
12728  * <p>Here's an example showing the creation of a typical Resizable:</p>
12729  * <pre><code>
12730 var resizer = new Roo.Resizable("element-id", {
12731     handles: 'all',
12732     minWidth: 200,
12733     minHeight: 100,
12734     maxWidth: 500,
12735     maxHeight: 400,
12736     pinned: true
12737 });
12738 resizer.on("resize", myHandler);
12739 </code></pre>
12740  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12741  * resizer.east.setDisplayed(false);</p>
12742  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12743  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12744  * resize operation's new size (defaults to [0, 0])
12745  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12746  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12747  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12748  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12749  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12750  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12751  * @cfg {Number} width The width of the element in pixels (defaults to null)
12752  * @cfg {Number} height The height of the element in pixels (defaults to null)
12753  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12754  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12755  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12756  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12757  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12758  * in favor of the handles config option (defaults to false)
12759  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12760  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12761  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12762  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12763  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12764  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12765  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12766  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12767  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12768  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12769  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12770  * @constructor
12771  * Create a new resizable component
12772  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12773  * @param {Object} config configuration options
12774   */
12775 Roo.Resizable = function(el, config)
12776 {
12777     this.el = Roo.get(el);
12778
12779     if(config && config.wrap){
12780         config.resizeChild = this.el;
12781         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12782         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12783         this.el.setStyle("overflow", "hidden");
12784         this.el.setPositioning(config.resizeChild.getPositioning());
12785         config.resizeChild.clearPositioning();
12786         if(!config.width || !config.height){
12787             var csize = config.resizeChild.getSize();
12788             this.el.setSize(csize.width, csize.height);
12789         }
12790         if(config.pinned && !config.adjustments){
12791             config.adjustments = "auto";
12792         }
12793     }
12794
12795     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12796     this.proxy.unselectable();
12797     this.proxy.enableDisplayMode('block');
12798
12799     Roo.apply(this, config);
12800
12801     if(this.pinned){
12802         this.disableTrackOver = true;
12803         this.el.addClass("x-resizable-pinned");
12804     }
12805     // if the element isn't positioned, make it relative
12806     var position = this.el.getStyle("position");
12807     if(position != "absolute" && position != "fixed"){
12808         this.el.setStyle("position", "relative");
12809     }
12810     if(!this.handles){ // no handles passed, must be legacy style
12811         this.handles = 's,e,se';
12812         if(this.multiDirectional){
12813             this.handles += ',n,w';
12814         }
12815     }
12816     if(this.handles == "all"){
12817         this.handles = "n s e w ne nw se sw";
12818     }
12819     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12820     var ps = Roo.Resizable.positions;
12821     for(var i = 0, len = hs.length; i < len; i++){
12822         if(hs[i] && ps[hs[i]]){
12823             var pos = ps[hs[i]];
12824             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12825         }
12826     }
12827     // legacy
12828     this.corner = this.southeast;
12829     
12830     // updateBox = the box can move..
12831     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12832         this.updateBox = true;
12833     }
12834
12835     this.activeHandle = null;
12836
12837     if(this.resizeChild){
12838         if(typeof this.resizeChild == "boolean"){
12839             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12840         }else{
12841             this.resizeChild = Roo.get(this.resizeChild, true);
12842         }
12843     }
12844     
12845     if(this.adjustments == "auto"){
12846         var rc = this.resizeChild;
12847         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12848         if(rc && (hw || hn)){
12849             rc.position("relative");
12850             rc.setLeft(hw ? hw.el.getWidth() : 0);
12851             rc.setTop(hn ? hn.el.getHeight() : 0);
12852         }
12853         this.adjustments = [
12854             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12855             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12856         ];
12857     }
12858
12859     if(this.draggable){
12860         this.dd = this.dynamic ?
12861             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12862         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12863     }
12864
12865     // public events
12866     this.addEvents({
12867         /**
12868          * @event beforeresize
12869          * Fired before resize is allowed. Set enabled to false to cancel resize.
12870          * @param {Roo.Resizable} this
12871          * @param {Roo.EventObject} e The mousedown event
12872          */
12873         "beforeresize" : true,
12874         /**
12875          * @event resizing
12876          * Fired a resizing.
12877          * @param {Roo.Resizable} this
12878          * @param {Number} x The new x position
12879          * @param {Number} y The new y position
12880          * @param {Number} w The new w width
12881          * @param {Number} h The new h hight
12882          * @param {Roo.EventObject} e The mouseup event
12883          */
12884         "resizing" : true,
12885         /**
12886          * @event resize
12887          * Fired after a resize.
12888          * @param {Roo.Resizable} this
12889          * @param {Number} width The new width
12890          * @param {Number} height The new height
12891          * @param {Roo.EventObject} e The mouseup event
12892          */
12893         "resize" : true
12894     });
12895
12896     if(this.width !== null && this.height !== null){
12897         this.resizeTo(this.width, this.height);
12898     }else{
12899         this.updateChildSize();
12900     }
12901     if(Roo.isIE){
12902         this.el.dom.style.zoom = 1;
12903     }
12904     Roo.Resizable.superclass.constructor.call(this);
12905 };
12906
12907 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12908         resizeChild : false,
12909         adjustments : [0, 0],
12910         minWidth : 5,
12911         minHeight : 5,
12912         maxWidth : 10000,
12913         maxHeight : 10000,
12914         enabled : true,
12915         animate : false,
12916         duration : .35,
12917         dynamic : false,
12918         handles : false,
12919         multiDirectional : false,
12920         disableTrackOver : false,
12921         easing : 'easeOutStrong',
12922         widthIncrement : 0,
12923         heightIncrement : 0,
12924         pinned : false,
12925         width : null,
12926         height : null,
12927         preserveRatio : false,
12928         transparent: false,
12929         minX: 0,
12930         minY: 0,
12931         draggable: false,
12932
12933         /**
12934          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12935          */
12936         constrainTo: undefined,
12937         /**
12938          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12939          */
12940         resizeRegion: undefined,
12941
12942
12943     /**
12944      * Perform a manual resize
12945      * @param {Number} width
12946      * @param {Number} height
12947      */
12948     resizeTo : function(width, height){
12949         this.el.setSize(width, height);
12950         this.updateChildSize();
12951         this.fireEvent("resize", this, width, height, null);
12952     },
12953
12954     // private
12955     startSizing : function(e, handle){
12956         this.fireEvent("beforeresize", this, e);
12957         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12958
12959             if(!this.overlay){
12960                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12961                 this.overlay.unselectable();
12962                 this.overlay.enableDisplayMode("block");
12963                 this.overlay.on("mousemove", this.onMouseMove, this);
12964                 this.overlay.on("mouseup", this.onMouseUp, this);
12965             }
12966             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12967
12968             this.resizing = true;
12969             this.startBox = this.el.getBox();
12970             this.startPoint = e.getXY();
12971             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12972                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12973
12974             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12975             this.overlay.show();
12976
12977             if(this.constrainTo) {
12978                 var ct = Roo.get(this.constrainTo);
12979                 this.resizeRegion = ct.getRegion().adjust(
12980                     ct.getFrameWidth('t'),
12981                     ct.getFrameWidth('l'),
12982                     -ct.getFrameWidth('b'),
12983                     -ct.getFrameWidth('r')
12984                 );
12985             }
12986
12987             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12988             this.proxy.show();
12989             this.proxy.setBox(this.startBox);
12990             if(!this.dynamic){
12991                 this.proxy.setStyle('visibility', 'visible');
12992             }
12993         }
12994     },
12995
12996     // private
12997     onMouseDown : function(handle, e){
12998         if(this.enabled){
12999             e.stopEvent();
13000             this.activeHandle = handle;
13001             this.startSizing(e, handle);
13002         }
13003     },
13004
13005     // private
13006     onMouseUp : function(e){
13007         var size = this.resizeElement();
13008         this.resizing = false;
13009         this.handleOut();
13010         this.overlay.hide();
13011         this.proxy.hide();
13012         this.fireEvent("resize", this, size.width, size.height, e);
13013     },
13014
13015     // private
13016     updateChildSize : function(){
13017         
13018         if(this.resizeChild){
13019             var el = this.el;
13020             var child = this.resizeChild;
13021             var adj = this.adjustments;
13022             if(el.dom.offsetWidth){
13023                 var b = el.getSize(true);
13024                 child.setSize(b.width+adj[0], b.height+adj[1]);
13025             }
13026             // Second call here for IE
13027             // The first call enables instant resizing and
13028             // the second call corrects scroll bars if they
13029             // exist
13030             if(Roo.isIE){
13031                 setTimeout(function(){
13032                     if(el.dom.offsetWidth){
13033                         var b = el.getSize(true);
13034                         child.setSize(b.width+adj[0], b.height+adj[1]);
13035                     }
13036                 }, 10);
13037             }
13038         }
13039     },
13040
13041     // private
13042     snap : function(value, inc, min){
13043         if(!inc || !value) return value;
13044         var newValue = value;
13045         var m = value % inc;
13046         if(m > 0){
13047             if(m > (inc/2)){
13048                 newValue = value + (inc-m);
13049             }else{
13050                 newValue = value - m;
13051             }
13052         }
13053         return Math.max(min, newValue);
13054     },
13055
13056     // private
13057     resizeElement : function(){
13058         var box = this.proxy.getBox();
13059         if(this.updateBox){
13060             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13061         }else{
13062             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13063         }
13064         this.updateChildSize();
13065         if(!this.dynamic){
13066             this.proxy.hide();
13067         }
13068         return box;
13069     },
13070
13071     // private
13072     constrain : function(v, diff, m, mx){
13073         if(v - diff < m){
13074             diff = v - m;
13075         }else if(v - diff > mx){
13076             diff = mx - v;
13077         }
13078         return diff;
13079     },
13080
13081     // private
13082     onMouseMove : function(e){
13083         
13084         if(this.enabled){
13085             try{// try catch so if something goes wrong the user doesn't get hung
13086
13087             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13088                 return;
13089             }
13090
13091             //var curXY = this.startPoint;
13092             var curSize = this.curSize || this.startBox;
13093             var x = this.startBox.x, y = this.startBox.y;
13094             var ox = x, oy = y;
13095             var w = curSize.width, h = curSize.height;
13096             var ow = w, oh = h;
13097             var mw = this.minWidth, mh = this.minHeight;
13098             var mxw = this.maxWidth, mxh = this.maxHeight;
13099             var wi = this.widthIncrement;
13100             var hi = this.heightIncrement;
13101
13102             var eventXY = e.getXY();
13103             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13104             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13105
13106             var pos = this.activeHandle.position;
13107
13108             switch(pos){
13109                 case "east":
13110                     w += diffX;
13111                     w = Math.min(Math.max(mw, w), mxw);
13112                     break;
13113              
13114                 case "south":
13115                     h += diffY;
13116                     h = Math.min(Math.max(mh, h), mxh);
13117                     break;
13118                 case "southeast":
13119                     w += diffX;
13120                     h += diffY;
13121                     w = Math.min(Math.max(mw, w), mxw);
13122                     h = Math.min(Math.max(mh, h), mxh);
13123                     break;
13124                 case "north":
13125                     diffY = this.constrain(h, diffY, mh, mxh);
13126                     y += diffY;
13127                     h -= diffY;
13128                     break;
13129                 case "hdrag":
13130                     
13131                     if (wi) {
13132                         var adiffX = Math.abs(diffX);
13133                         var sub = (adiffX % wi); // how much 
13134                         if (sub > (wi/2)) { // far enough to snap
13135                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13136                         } else {
13137                             // remove difference.. 
13138                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13139                         }
13140                     }
13141                     x += diffX;
13142                     x = Math.max(this.minX, x);
13143                     break;
13144                 case "west":
13145                     diffX = this.constrain(w, diffX, mw, mxw);
13146                     x += diffX;
13147                     w -= diffX;
13148                     break;
13149                 case "northeast":
13150                     w += diffX;
13151                     w = Math.min(Math.max(mw, w), mxw);
13152                     diffY = this.constrain(h, diffY, mh, mxh);
13153                     y += diffY;
13154                     h -= diffY;
13155                     break;
13156                 case "northwest":
13157                     diffX = this.constrain(w, diffX, mw, mxw);
13158                     diffY = this.constrain(h, diffY, mh, mxh);
13159                     y += diffY;
13160                     h -= diffY;
13161                     x += diffX;
13162                     w -= diffX;
13163                     break;
13164                case "southwest":
13165                     diffX = this.constrain(w, diffX, mw, mxw);
13166                     h += diffY;
13167                     h = Math.min(Math.max(mh, h), mxh);
13168                     x += diffX;
13169                     w -= diffX;
13170                     break;
13171             }
13172
13173             var sw = this.snap(w, wi, mw);
13174             var sh = this.snap(h, hi, mh);
13175             if(sw != w || sh != h){
13176                 switch(pos){
13177                     case "northeast":
13178                         y -= sh - h;
13179                     break;
13180                     case "north":
13181                         y -= sh - h;
13182                         break;
13183                     case "southwest":
13184                         x -= sw - w;
13185                     break;
13186                     case "west":
13187                         x -= sw - w;
13188                         break;
13189                     case "northwest":
13190                         x -= sw - w;
13191                         y -= sh - h;
13192                     break;
13193                 }
13194                 w = sw;
13195                 h = sh;
13196             }
13197
13198             if(this.preserveRatio){
13199                 switch(pos){
13200                     case "southeast":
13201                     case "east":
13202                         h = oh * (w/ow);
13203                         h = Math.min(Math.max(mh, h), mxh);
13204                         w = ow * (h/oh);
13205                        break;
13206                     case "south":
13207                         w = ow * (h/oh);
13208                         w = Math.min(Math.max(mw, w), mxw);
13209                         h = oh * (w/ow);
13210                         break;
13211                     case "northeast":
13212                         w = ow * (h/oh);
13213                         w = Math.min(Math.max(mw, w), mxw);
13214                         h = oh * (w/ow);
13215                     break;
13216                     case "north":
13217                         var tw = w;
13218                         w = ow * (h/oh);
13219                         w = Math.min(Math.max(mw, w), mxw);
13220                         h = oh * (w/ow);
13221                         x += (tw - w) / 2;
13222                         break;
13223                     case "southwest":
13224                         h = oh * (w/ow);
13225                         h = Math.min(Math.max(mh, h), mxh);
13226                         var tw = w;
13227                         w = ow * (h/oh);
13228                         x += tw - w;
13229                         break;
13230                     case "west":
13231                         var th = h;
13232                         h = oh * (w/ow);
13233                         h = Math.min(Math.max(mh, h), mxh);
13234                         y += (th - h) / 2;
13235                         var tw = w;
13236                         w = ow * (h/oh);
13237                         x += tw - w;
13238                        break;
13239                     case "northwest":
13240                         var tw = w;
13241                         var th = h;
13242                         h = oh * (w/ow);
13243                         h = Math.min(Math.max(mh, h), mxh);
13244                         w = ow * (h/oh);
13245                         y += th - h;
13246                         x += tw - w;
13247                        break;
13248
13249                 }
13250             }
13251             if (pos == 'hdrag') {
13252                 w = ow;
13253             }
13254             this.proxy.setBounds(x, y, w, h);
13255             if(this.dynamic){
13256                 this.resizeElement();
13257             }
13258             }catch(e){}
13259         }
13260         this.fireEvent("resizing", this, x, y, w, h, e);
13261     },
13262
13263     // private
13264     handleOver : function(){
13265         if(this.enabled){
13266             this.el.addClass("x-resizable-over");
13267         }
13268     },
13269
13270     // private
13271     handleOut : function(){
13272         if(!this.resizing){
13273             this.el.removeClass("x-resizable-over");
13274         }
13275     },
13276
13277     /**
13278      * Returns the element this component is bound to.
13279      * @return {Roo.Element}
13280      */
13281     getEl : function(){
13282         return this.el;
13283     },
13284
13285     /**
13286      * Returns the resizeChild element (or null).
13287      * @return {Roo.Element}
13288      */
13289     getResizeChild : function(){
13290         return this.resizeChild;
13291     },
13292     groupHandler : function()
13293     {
13294         
13295     },
13296     /**
13297      * Destroys this resizable. If the element was wrapped and
13298      * removeEl is not true then the element remains.
13299      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13300      */
13301     destroy : function(removeEl){
13302         this.proxy.remove();
13303         if(this.overlay){
13304             this.overlay.removeAllListeners();
13305             this.overlay.remove();
13306         }
13307         var ps = Roo.Resizable.positions;
13308         for(var k in ps){
13309             if(typeof ps[k] != "function" && this[ps[k]]){
13310                 var h = this[ps[k]];
13311                 h.el.removeAllListeners();
13312                 h.el.remove();
13313             }
13314         }
13315         if(removeEl){
13316             this.el.update("");
13317             this.el.remove();
13318         }
13319     }
13320 });
13321
13322 // private
13323 // hash to map config positions to true positions
13324 Roo.Resizable.positions = {
13325     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13326     hd: "hdrag"
13327 };
13328
13329 // private
13330 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13331     if(!this.tpl){
13332         // only initialize the template if resizable is used
13333         var tpl = Roo.DomHelper.createTemplate(
13334             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13335         );
13336         tpl.compile();
13337         Roo.Resizable.Handle.prototype.tpl = tpl;
13338     }
13339     this.position = pos;
13340     this.rz = rz;
13341     // show north drag fro topdra
13342     var handlepos = pos == 'hdrag' ? 'north' : pos;
13343     
13344     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13345     if (pos == 'hdrag') {
13346         this.el.setStyle('cursor', 'pointer');
13347     }
13348     this.el.unselectable();
13349     if(transparent){
13350         this.el.setOpacity(0);
13351     }
13352     this.el.on("mousedown", this.onMouseDown, this);
13353     if(!disableTrackOver){
13354         this.el.on("mouseover", this.onMouseOver, this);
13355         this.el.on("mouseout", this.onMouseOut, this);
13356     }
13357 };
13358
13359 // private
13360 Roo.Resizable.Handle.prototype = {
13361     afterResize : function(rz){
13362         Roo.log('after?');
13363         // do nothing
13364     },
13365     // private
13366     onMouseDown : function(e){
13367         this.rz.onMouseDown(this, e);
13368     },
13369     // private
13370     onMouseOver : function(e){
13371         this.rz.handleOver(this, e);
13372     },
13373     // private
13374     onMouseOut : function(e){
13375         this.rz.handleOut(this, e);
13376     }
13377 };/*
13378  * Based on:
13379  * Ext JS Library 1.1.1
13380  * Copyright(c) 2006-2007, Ext JS, LLC.
13381  *
13382  * Originally Released Under LGPL - original licence link has changed is not relivant.
13383  *
13384  * Fork - LGPL
13385  * <script type="text/javascript">
13386  */
13387
13388 /**
13389  * @class Roo.Editor
13390  * @extends Roo.Component
13391  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13392  * @constructor
13393  * Create a new Editor
13394  * @param {Roo.form.Field} field The Field object (or descendant)
13395  * @param {Object} config The config object
13396  */
13397 Roo.Editor = function(field, config){
13398     Roo.Editor.superclass.constructor.call(this, config);
13399     this.field = field;
13400     this.addEvents({
13401         /**
13402              * @event beforestartedit
13403              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13404              * false from the handler of this event.
13405              * @param {Editor} this
13406              * @param {Roo.Element} boundEl The underlying element bound to this editor
13407              * @param {Mixed} value The field value being set
13408              */
13409         "beforestartedit" : true,
13410         /**
13411              * @event startedit
13412              * Fires when this editor is displayed
13413              * @param {Roo.Element} boundEl The underlying element bound to this editor
13414              * @param {Mixed} value The starting field value
13415              */
13416         "startedit" : true,
13417         /**
13418              * @event beforecomplete
13419              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13420              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13421              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13422              * event will not fire since no edit actually occurred.
13423              * @param {Editor} this
13424              * @param {Mixed} value The current field value
13425              * @param {Mixed} startValue The original field value
13426              */
13427         "beforecomplete" : true,
13428         /**
13429              * @event complete
13430              * Fires after editing is complete and any changed value has been written to the underlying field.
13431              * @param {Editor} this
13432              * @param {Mixed} value The current field value
13433              * @param {Mixed} startValue The original field value
13434              */
13435         "complete" : true,
13436         /**
13437          * @event specialkey
13438          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13439          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13440          * @param {Roo.form.Field} this
13441          * @param {Roo.EventObject} e The event object
13442          */
13443         "specialkey" : true
13444     });
13445 };
13446
13447 Roo.extend(Roo.Editor, Roo.Component, {
13448     /**
13449      * @cfg {Boolean/String} autosize
13450      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13451      * or "height" to adopt the height only (defaults to false)
13452      */
13453     /**
13454      * @cfg {Boolean} revertInvalid
13455      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13456      * validation fails (defaults to true)
13457      */
13458     /**
13459      * @cfg {Boolean} ignoreNoChange
13460      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13461      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13462      * will never be ignored.
13463      */
13464     /**
13465      * @cfg {Boolean} hideEl
13466      * False to keep the bound element visible while the editor is displayed (defaults to true)
13467      */
13468     /**
13469      * @cfg {Mixed} value
13470      * The data value of the underlying field (defaults to "")
13471      */
13472     value : "",
13473     /**
13474      * @cfg {String} alignment
13475      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13476      */
13477     alignment: "c-c?",
13478     /**
13479      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13480      * for bottom-right shadow (defaults to "frame")
13481      */
13482     shadow : "frame",
13483     /**
13484      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13485      */
13486     constrain : false,
13487     /**
13488      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13489      */
13490     completeOnEnter : false,
13491     /**
13492      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13493      */
13494     cancelOnEsc : false,
13495     /**
13496      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13497      */
13498     updateEl : false,
13499
13500     // private
13501     onRender : function(ct, position){
13502         this.el = new Roo.Layer({
13503             shadow: this.shadow,
13504             cls: "x-editor",
13505             parentEl : ct,
13506             shim : this.shim,
13507             shadowOffset:4,
13508             id: this.id,
13509             constrain: this.constrain
13510         });
13511         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13512         if(this.field.msgTarget != 'title'){
13513             this.field.msgTarget = 'qtip';
13514         }
13515         this.field.render(this.el);
13516         if(Roo.isGecko){
13517             this.field.el.dom.setAttribute('autocomplete', 'off');
13518         }
13519         this.field.on("specialkey", this.onSpecialKey, this);
13520         if(this.swallowKeys){
13521             this.field.el.swallowEvent(['keydown','keypress']);
13522         }
13523         this.field.show();
13524         this.field.on("blur", this.onBlur, this);
13525         if(this.field.grow){
13526             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13527         }
13528     },
13529
13530     onSpecialKey : function(field, e)
13531     {
13532         //Roo.log('editor onSpecialKey');
13533         if(this.completeOnEnter && e.getKey() == e.ENTER){
13534             e.stopEvent();
13535             this.completeEdit();
13536             return;
13537         }
13538         // do not fire special key otherwise it might hide close the editor...
13539         if(e.getKey() == e.ENTER){    
13540             return;
13541         }
13542         if(this.cancelOnEsc && e.getKey() == e.ESC){
13543             this.cancelEdit();
13544             return;
13545         } 
13546         this.fireEvent('specialkey', field, e);
13547     
13548     },
13549
13550     /**
13551      * Starts the editing process and shows the editor.
13552      * @param {String/HTMLElement/Element} el The element to edit
13553      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13554       * to the innerHTML of el.
13555      */
13556     startEdit : function(el, value){
13557         if(this.editing){
13558             this.completeEdit();
13559         }
13560         this.boundEl = Roo.get(el);
13561         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13562         if(!this.rendered){
13563             this.render(this.parentEl || document.body);
13564         }
13565         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13566             return;
13567         }
13568         this.startValue = v;
13569         this.field.setValue(v);
13570         if(this.autoSize){
13571             var sz = this.boundEl.getSize();
13572             switch(this.autoSize){
13573                 case "width":
13574                 this.setSize(sz.width,  "");
13575                 break;
13576                 case "height":
13577                 this.setSize("",  sz.height);
13578                 break;
13579                 default:
13580                 this.setSize(sz.width,  sz.height);
13581             }
13582         }
13583         this.el.alignTo(this.boundEl, this.alignment);
13584         this.editing = true;
13585         if(Roo.QuickTips){
13586             Roo.QuickTips.disable();
13587         }
13588         this.show();
13589     },
13590
13591     /**
13592      * Sets the height and width of this editor.
13593      * @param {Number} width The new width
13594      * @param {Number} height The new height
13595      */
13596     setSize : function(w, h){
13597         this.field.setSize(w, h);
13598         if(this.el){
13599             this.el.sync();
13600         }
13601     },
13602
13603     /**
13604      * Realigns the editor to the bound field based on the current alignment config value.
13605      */
13606     realign : function(){
13607         this.el.alignTo(this.boundEl, this.alignment);
13608     },
13609
13610     /**
13611      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13612      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13613      */
13614     completeEdit : function(remainVisible){
13615         if(!this.editing){
13616             return;
13617         }
13618         var v = this.getValue();
13619         if(this.revertInvalid !== false && !this.field.isValid()){
13620             v = this.startValue;
13621             this.cancelEdit(true);
13622         }
13623         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13624             this.editing = false;
13625             this.hide();
13626             return;
13627         }
13628         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13629             this.editing = false;
13630             if(this.updateEl && this.boundEl){
13631                 this.boundEl.update(v);
13632             }
13633             if(remainVisible !== true){
13634                 this.hide();
13635             }
13636             this.fireEvent("complete", this, v, this.startValue);
13637         }
13638     },
13639
13640     // private
13641     onShow : function(){
13642         this.el.show();
13643         if(this.hideEl !== false){
13644             this.boundEl.hide();
13645         }
13646         this.field.show();
13647         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13648             this.fixIEFocus = true;
13649             this.deferredFocus.defer(50, this);
13650         }else{
13651             this.field.focus();
13652         }
13653         this.fireEvent("startedit", this.boundEl, this.startValue);
13654     },
13655
13656     deferredFocus : function(){
13657         if(this.editing){
13658             this.field.focus();
13659         }
13660     },
13661
13662     /**
13663      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13664      * reverted to the original starting value.
13665      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13666      * cancel (defaults to false)
13667      */
13668     cancelEdit : function(remainVisible){
13669         if(this.editing){
13670             this.setValue(this.startValue);
13671             if(remainVisible !== true){
13672                 this.hide();
13673             }
13674         }
13675     },
13676
13677     // private
13678     onBlur : function(){
13679         if(this.allowBlur !== true && this.editing){
13680             this.completeEdit();
13681         }
13682     },
13683
13684     // private
13685     onHide : function(){
13686         if(this.editing){
13687             this.completeEdit();
13688             return;
13689         }
13690         this.field.blur();
13691         if(this.field.collapse){
13692             this.field.collapse();
13693         }
13694         this.el.hide();
13695         if(this.hideEl !== false){
13696             this.boundEl.show();
13697         }
13698         if(Roo.QuickTips){
13699             Roo.QuickTips.enable();
13700         }
13701     },
13702
13703     /**
13704      * Sets the data value of the editor
13705      * @param {Mixed} value Any valid value supported by the underlying field
13706      */
13707     setValue : function(v){
13708         this.field.setValue(v);
13709     },
13710
13711     /**
13712      * Gets the data value of the editor
13713      * @return {Mixed} The data value
13714      */
13715     getValue : function(){
13716         return this.field.getValue();
13717     }
13718 });/*
13719  * Based on:
13720  * Ext JS Library 1.1.1
13721  * Copyright(c) 2006-2007, Ext JS, LLC.
13722  *
13723  * Originally Released Under LGPL - original licence link has changed is not relivant.
13724  *
13725  * Fork - LGPL
13726  * <script type="text/javascript">
13727  */
13728  
13729 /**
13730  * @class Roo.BasicDialog
13731  * @extends Roo.util.Observable
13732  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13733  * <pre><code>
13734 var dlg = new Roo.BasicDialog("my-dlg", {
13735     height: 200,
13736     width: 300,
13737     minHeight: 100,
13738     minWidth: 150,
13739     modal: true,
13740     proxyDrag: true,
13741     shadow: true
13742 });
13743 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13744 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13745 dlg.addButton('Cancel', dlg.hide, dlg);
13746 dlg.show();
13747 </code></pre>
13748   <b>A Dialog should always be a direct child of the body element.</b>
13749  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13750  * @cfg {String} title Default text to display in the title bar (defaults to null)
13751  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13752  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13753  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13754  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13755  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13756  * (defaults to null with no animation)
13757  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13758  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13759  * property for valid values (defaults to 'all')
13760  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13761  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13762  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13763  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13764  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13765  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13766  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13767  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13768  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13769  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13770  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13771  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13772  * draggable = true (defaults to false)
13773  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13774  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13775  * shadow (defaults to false)
13776  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13777  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13778  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13779  * @cfg {Array} buttons Array of buttons
13780  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13781  * @constructor
13782  * Create a new BasicDialog.
13783  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13784  * @param {Object} config Configuration options
13785  */
13786 Roo.BasicDialog = function(el, config){
13787     this.el = Roo.get(el);
13788     var dh = Roo.DomHelper;
13789     if(!this.el && config && config.autoCreate){
13790         if(typeof config.autoCreate == "object"){
13791             if(!config.autoCreate.id){
13792                 config.autoCreate.id = el;
13793             }
13794             this.el = dh.append(document.body,
13795                         config.autoCreate, true);
13796         }else{
13797             this.el = dh.append(document.body,
13798                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13799         }
13800     }
13801     el = this.el;
13802     el.setDisplayed(true);
13803     el.hide = this.hideAction;
13804     this.id = el.id;
13805     el.addClass("x-dlg");
13806
13807     Roo.apply(this, config);
13808
13809     this.proxy = el.createProxy("x-dlg-proxy");
13810     this.proxy.hide = this.hideAction;
13811     this.proxy.setOpacity(.5);
13812     this.proxy.hide();
13813
13814     if(config.width){
13815         el.setWidth(config.width);
13816     }
13817     if(config.height){
13818         el.setHeight(config.height);
13819     }
13820     this.size = el.getSize();
13821     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13822         this.xy = [config.x,config.y];
13823     }else{
13824         this.xy = el.getCenterXY(true);
13825     }
13826     /** The header element @type Roo.Element */
13827     this.header = el.child("> .x-dlg-hd");
13828     /** The body element @type Roo.Element */
13829     this.body = el.child("> .x-dlg-bd");
13830     /** The footer element @type Roo.Element */
13831     this.footer = el.child("> .x-dlg-ft");
13832
13833     if(!this.header){
13834         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13835     }
13836     if(!this.body){
13837         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13838     }
13839
13840     this.header.unselectable();
13841     if(this.title){
13842         this.header.update(this.title);
13843     }
13844     // this element allows the dialog to be focused for keyboard event
13845     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13846     this.focusEl.swallowEvent("click", true);
13847
13848     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13849
13850     // wrap the body and footer for special rendering
13851     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13852     if(this.footer){
13853         this.bwrap.dom.appendChild(this.footer.dom);
13854     }
13855
13856     this.bg = this.el.createChild({
13857         tag: "div", cls:"x-dlg-bg",
13858         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13859     });
13860     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13861
13862
13863     if(this.autoScroll !== false && !this.autoTabs){
13864         this.body.setStyle("overflow", "auto");
13865     }
13866
13867     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13868
13869     if(this.closable !== false){
13870         this.el.addClass("x-dlg-closable");
13871         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13872         this.close.on("click", this.closeClick, this);
13873         this.close.addClassOnOver("x-dlg-close-over");
13874     }
13875     if(this.collapsible !== false){
13876         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13877         this.collapseBtn.on("click", this.collapseClick, this);
13878         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13879         this.header.on("dblclick", this.collapseClick, this);
13880     }
13881     if(this.resizable !== false){
13882         this.el.addClass("x-dlg-resizable");
13883         this.resizer = new Roo.Resizable(el, {
13884             minWidth: this.minWidth || 80,
13885             minHeight:this.minHeight || 80,
13886             handles: this.resizeHandles || "all",
13887             pinned: true
13888         });
13889         this.resizer.on("beforeresize", this.beforeResize, this);
13890         this.resizer.on("resize", this.onResize, this);
13891     }
13892     if(this.draggable !== false){
13893         el.addClass("x-dlg-draggable");
13894         if (!this.proxyDrag) {
13895             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13896         }
13897         else {
13898             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13899         }
13900         dd.setHandleElId(this.header.id);
13901         dd.endDrag = this.endMove.createDelegate(this);
13902         dd.startDrag = this.startMove.createDelegate(this);
13903         dd.onDrag = this.onDrag.createDelegate(this);
13904         dd.scroll = false;
13905         this.dd = dd;
13906     }
13907     if(this.modal){
13908         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13909         this.mask.enableDisplayMode("block");
13910         this.mask.hide();
13911         this.el.addClass("x-dlg-modal");
13912     }
13913     if(this.shadow){
13914         this.shadow = new Roo.Shadow({
13915             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13916             offset : this.shadowOffset
13917         });
13918     }else{
13919         this.shadowOffset = 0;
13920     }
13921     if(Roo.useShims && this.shim !== false){
13922         this.shim = this.el.createShim();
13923         this.shim.hide = this.hideAction;
13924         this.shim.hide();
13925     }else{
13926         this.shim = false;
13927     }
13928     if(this.autoTabs){
13929         this.initTabs();
13930     }
13931     if (this.buttons) { 
13932         var bts= this.buttons;
13933         this.buttons = [];
13934         Roo.each(bts, function(b) {
13935             this.addButton(b);
13936         }, this);
13937     }
13938     
13939     
13940     this.addEvents({
13941         /**
13942          * @event keydown
13943          * Fires when a key is pressed
13944          * @param {Roo.BasicDialog} this
13945          * @param {Roo.EventObject} e
13946          */
13947         "keydown" : true,
13948         /**
13949          * @event move
13950          * Fires when this dialog is moved by the user.
13951          * @param {Roo.BasicDialog} this
13952          * @param {Number} x The new page X
13953          * @param {Number} y The new page Y
13954          */
13955         "move" : true,
13956         /**
13957          * @event resize
13958          * Fires when this dialog is resized by the user.
13959          * @param {Roo.BasicDialog} this
13960          * @param {Number} width The new width
13961          * @param {Number} height The new height
13962          */
13963         "resize" : true,
13964         /**
13965          * @event beforehide
13966          * Fires before this dialog is hidden.
13967          * @param {Roo.BasicDialog} this
13968          */
13969         "beforehide" : true,
13970         /**
13971          * @event hide
13972          * Fires when this dialog is hidden.
13973          * @param {Roo.BasicDialog} this
13974          */
13975         "hide" : true,
13976         /**
13977          * @event beforeshow
13978          * Fires before this dialog is shown.
13979          * @param {Roo.BasicDialog} this
13980          */
13981         "beforeshow" : true,
13982         /**
13983          * @event show
13984          * Fires when this dialog is shown.
13985          * @param {Roo.BasicDialog} this
13986          */
13987         "show" : true
13988     });
13989     el.on("keydown", this.onKeyDown, this);
13990     el.on("mousedown", this.toFront, this);
13991     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13992     this.el.hide();
13993     Roo.DialogManager.register(this);
13994     Roo.BasicDialog.superclass.constructor.call(this);
13995 };
13996
13997 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13998     shadowOffset: Roo.isIE ? 6 : 5,
13999     minHeight: 80,
14000     minWidth: 200,
14001     minButtonWidth: 75,
14002     defaultButton: null,
14003     buttonAlign: "right",
14004     tabTag: 'div',
14005     firstShow: true,
14006
14007     /**
14008      * Sets the dialog title text
14009      * @param {String} text The title text to display
14010      * @return {Roo.BasicDialog} this
14011      */
14012     setTitle : function(text){
14013         this.header.update(text);
14014         return this;
14015     },
14016
14017     // private
14018     closeClick : function(){
14019         this.hide();
14020     },
14021
14022     // private
14023     collapseClick : function(){
14024         this[this.collapsed ? "expand" : "collapse"]();
14025     },
14026
14027     /**
14028      * Collapses the dialog to its minimized state (only the title bar is visible).
14029      * Equivalent to the user clicking the collapse dialog button.
14030      */
14031     collapse : function(){
14032         if(!this.collapsed){
14033             this.collapsed = true;
14034             this.el.addClass("x-dlg-collapsed");
14035             this.restoreHeight = this.el.getHeight();
14036             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14037         }
14038     },
14039
14040     /**
14041      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14042      * clicking the expand dialog button.
14043      */
14044     expand : function(){
14045         if(this.collapsed){
14046             this.collapsed = false;
14047             this.el.removeClass("x-dlg-collapsed");
14048             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14049         }
14050     },
14051
14052     /**
14053      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14054      * @return {Roo.TabPanel} The tabs component
14055      */
14056     initTabs : function(){
14057         var tabs = this.getTabs();
14058         while(tabs.getTab(0)){
14059             tabs.removeTab(0);
14060         }
14061         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14062             var dom = el.dom;
14063             tabs.addTab(Roo.id(dom), dom.title);
14064             dom.title = "";
14065         });
14066         tabs.activate(0);
14067         return tabs;
14068     },
14069
14070     // private
14071     beforeResize : function(){
14072         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14073     },
14074
14075     // private
14076     onResize : function(){
14077         this.refreshSize();
14078         this.syncBodyHeight();
14079         this.adjustAssets();
14080         this.focus();
14081         this.fireEvent("resize", this, this.size.width, this.size.height);
14082     },
14083
14084     // private
14085     onKeyDown : function(e){
14086         if(this.isVisible()){
14087             this.fireEvent("keydown", this, e);
14088         }
14089     },
14090
14091     /**
14092      * Resizes the dialog.
14093      * @param {Number} width
14094      * @param {Number} height
14095      * @return {Roo.BasicDialog} this
14096      */
14097     resizeTo : function(width, height){
14098         this.el.setSize(width, height);
14099         this.size = {width: width, height: height};
14100         this.syncBodyHeight();
14101         if(this.fixedcenter){
14102             this.center();
14103         }
14104         if(this.isVisible()){
14105             this.constrainXY();
14106             this.adjustAssets();
14107         }
14108         this.fireEvent("resize", this, width, height);
14109         return this;
14110     },
14111
14112
14113     /**
14114      * Resizes the dialog to fit the specified content size.
14115      * @param {Number} width
14116      * @param {Number} height
14117      * @return {Roo.BasicDialog} this
14118      */
14119     setContentSize : function(w, h){
14120         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14121         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14122         //if(!this.el.isBorderBox()){
14123             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14124             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14125         //}
14126         if(this.tabs){
14127             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14128             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14129         }
14130         this.resizeTo(w, h);
14131         return this;
14132     },
14133
14134     /**
14135      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14136      * executed in response to a particular key being pressed while the dialog is active.
14137      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14138      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14139      * @param {Function} fn The function to call
14140      * @param {Object} scope (optional) The scope of the function
14141      * @return {Roo.BasicDialog} this
14142      */
14143     addKeyListener : function(key, fn, scope){
14144         var keyCode, shift, ctrl, alt;
14145         if(typeof key == "object" && !(key instanceof Array)){
14146             keyCode = key["key"];
14147             shift = key["shift"];
14148             ctrl = key["ctrl"];
14149             alt = key["alt"];
14150         }else{
14151             keyCode = key;
14152         }
14153         var handler = function(dlg, e){
14154             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14155                 var k = e.getKey();
14156                 if(keyCode instanceof Array){
14157                     for(var i = 0, len = keyCode.length; i < len; i++){
14158                         if(keyCode[i] == k){
14159                           fn.call(scope || window, dlg, k, e);
14160                           return;
14161                         }
14162                     }
14163                 }else{
14164                     if(k == keyCode){
14165                         fn.call(scope || window, dlg, k, e);
14166                     }
14167                 }
14168             }
14169         };
14170         this.on("keydown", handler);
14171         return this;
14172     },
14173
14174     /**
14175      * Returns the TabPanel component (creates it if it doesn't exist).
14176      * Note: If you wish to simply check for the existence of tabs without creating them,
14177      * check for a null 'tabs' property.
14178      * @return {Roo.TabPanel} The tabs component
14179      */
14180     getTabs : function(){
14181         if(!this.tabs){
14182             this.el.addClass("x-dlg-auto-tabs");
14183             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14184             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14185         }
14186         return this.tabs;
14187     },
14188
14189     /**
14190      * Adds a button to the footer section of the dialog.
14191      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14192      * object or a valid Roo.DomHelper element config
14193      * @param {Function} handler The function called when the button is clicked
14194      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14195      * @return {Roo.Button} The new button
14196      */
14197     addButton : function(config, handler, scope){
14198         var dh = Roo.DomHelper;
14199         if(!this.footer){
14200             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14201         }
14202         if(!this.btnContainer){
14203             var tb = this.footer.createChild({
14204
14205                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14206                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14207             }, null, true);
14208             this.btnContainer = tb.firstChild.firstChild.firstChild;
14209         }
14210         var bconfig = {
14211             handler: handler,
14212             scope: scope,
14213             minWidth: this.minButtonWidth,
14214             hideParent:true
14215         };
14216         if(typeof config == "string"){
14217             bconfig.text = config;
14218         }else{
14219             if(config.tag){
14220                 bconfig.dhconfig = config;
14221             }else{
14222                 Roo.apply(bconfig, config);
14223             }
14224         }
14225         var fc = false;
14226         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14227             bconfig.position = Math.max(0, bconfig.position);
14228             fc = this.btnContainer.childNodes[bconfig.position];
14229         }
14230          
14231         var btn = new Roo.Button(
14232             fc ? 
14233                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14234                 : this.btnContainer.appendChild(document.createElement("td")),
14235             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14236             bconfig
14237         );
14238         this.syncBodyHeight();
14239         if(!this.buttons){
14240             /**
14241              * Array of all the buttons that have been added to this dialog via addButton
14242              * @type Array
14243              */
14244             this.buttons = [];
14245         }
14246         this.buttons.push(btn);
14247         return btn;
14248     },
14249
14250     /**
14251      * Sets the default button to be focused when the dialog is displayed.
14252      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14253      * @return {Roo.BasicDialog} this
14254      */
14255     setDefaultButton : function(btn){
14256         this.defaultButton = btn;
14257         return this;
14258     },
14259
14260     // private
14261     getHeaderFooterHeight : function(safe){
14262         var height = 0;
14263         if(this.header){
14264            height += this.header.getHeight();
14265         }
14266         if(this.footer){
14267            var fm = this.footer.getMargins();
14268             height += (this.footer.getHeight()+fm.top+fm.bottom);
14269         }
14270         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14271         height += this.centerBg.getPadding("tb");
14272         return height;
14273     },
14274
14275     // private
14276     syncBodyHeight : function()
14277     {
14278         var bd = this.body, // the text
14279             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14280             bw = this.bwrap;
14281         var height = this.size.height - this.getHeaderFooterHeight(false);
14282         bd.setHeight(height-bd.getMargins("tb"));
14283         var hh = this.header.getHeight();
14284         var h = this.size.height-hh;
14285         cb.setHeight(h);
14286         
14287         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14288         bw.setHeight(h-cb.getPadding("tb"));
14289         
14290         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14291         bd.setWidth(bw.getWidth(true));
14292         if(this.tabs){
14293             this.tabs.syncHeight();
14294             if(Roo.isIE){
14295                 this.tabs.el.repaint();
14296             }
14297         }
14298     },
14299
14300     /**
14301      * Restores the previous state of the dialog if Roo.state is configured.
14302      * @return {Roo.BasicDialog} this
14303      */
14304     restoreState : function(){
14305         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14306         if(box && box.width){
14307             this.xy = [box.x, box.y];
14308             this.resizeTo(box.width, box.height);
14309         }
14310         return this;
14311     },
14312
14313     // private
14314     beforeShow : function(){
14315         this.expand();
14316         if(this.fixedcenter){
14317             this.xy = this.el.getCenterXY(true);
14318         }
14319         if(this.modal){
14320             Roo.get(document.body).addClass("x-body-masked");
14321             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14322             this.mask.show();
14323         }
14324         this.constrainXY();
14325     },
14326
14327     // private
14328     animShow : function(){
14329         var b = Roo.get(this.animateTarget).getBox();
14330         this.proxy.setSize(b.width, b.height);
14331         this.proxy.setLocation(b.x, b.y);
14332         this.proxy.show();
14333         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14334                     true, .35, this.showEl.createDelegate(this));
14335     },
14336
14337     /**
14338      * Shows the dialog.
14339      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14340      * @return {Roo.BasicDialog} this
14341      */
14342     show : function(animateTarget){
14343         if (this.fireEvent("beforeshow", this) === false){
14344             return;
14345         }
14346         if(this.syncHeightBeforeShow){
14347             this.syncBodyHeight();
14348         }else if(this.firstShow){
14349             this.firstShow = false;
14350             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14351         }
14352         this.animateTarget = animateTarget || this.animateTarget;
14353         if(!this.el.isVisible()){
14354             this.beforeShow();
14355             if(this.animateTarget && Roo.get(this.animateTarget)){
14356                 this.animShow();
14357             }else{
14358                 this.showEl();
14359             }
14360         }
14361         return this;
14362     },
14363
14364     // private
14365     showEl : function(){
14366         this.proxy.hide();
14367         this.el.setXY(this.xy);
14368         this.el.show();
14369         this.adjustAssets(true);
14370         this.toFront();
14371         this.focus();
14372         // IE peekaboo bug - fix found by Dave Fenwick
14373         if(Roo.isIE){
14374             this.el.repaint();
14375         }
14376         this.fireEvent("show", this);
14377     },
14378
14379     /**
14380      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14381      * dialog itself will receive focus.
14382      */
14383     focus : function(){
14384         if(this.defaultButton){
14385             this.defaultButton.focus();
14386         }else{
14387             this.focusEl.focus();
14388         }
14389     },
14390
14391     // private
14392     constrainXY : function(){
14393         if(this.constraintoviewport !== false){
14394             if(!this.viewSize){
14395                 if(this.container){
14396                     var s = this.container.getSize();
14397                     this.viewSize = [s.width, s.height];
14398                 }else{
14399                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14400                 }
14401             }
14402             var s = Roo.get(this.container||document).getScroll();
14403
14404             var x = this.xy[0], y = this.xy[1];
14405             var w = this.size.width, h = this.size.height;
14406             var vw = this.viewSize[0], vh = this.viewSize[1];
14407             // only move it if it needs it
14408             var moved = false;
14409             // first validate right/bottom
14410             if(x + w > vw+s.left){
14411                 x = vw - w;
14412                 moved = true;
14413             }
14414             if(y + h > vh+s.top){
14415                 y = vh - h;
14416                 moved = true;
14417             }
14418             // then make sure top/left isn't negative
14419             if(x < s.left){
14420                 x = s.left;
14421                 moved = true;
14422             }
14423             if(y < s.top){
14424                 y = s.top;
14425                 moved = true;
14426             }
14427             if(moved){
14428                 // cache xy
14429                 this.xy = [x, y];
14430                 if(this.isVisible()){
14431                     this.el.setLocation(x, y);
14432                     this.adjustAssets();
14433                 }
14434             }
14435         }
14436     },
14437
14438     // private
14439     onDrag : function(){
14440         if(!this.proxyDrag){
14441             this.xy = this.el.getXY();
14442             this.adjustAssets();
14443         }
14444     },
14445
14446     // private
14447     adjustAssets : function(doShow){
14448         var x = this.xy[0], y = this.xy[1];
14449         var w = this.size.width, h = this.size.height;
14450         if(doShow === true){
14451             if(this.shadow){
14452                 this.shadow.show(this.el);
14453             }
14454             if(this.shim){
14455                 this.shim.show();
14456             }
14457         }
14458         if(this.shadow && this.shadow.isVisible()){
14459             this.shadow.show(this.el);
14460         }
14461         if(this.shim && this.shim.isVisible()){
14462             this.shim.setBounds(x, y, w, h);
14463         }
14464     },
14465
14466     // private
14467     adjustViewport : function(w, h){
14468         if(!w || !h){
14469             w = Roo.lib.Dom.getViewWidth();
14470             h = Roo.lib.Dom.getViewHeight();
14471         }
14472         // cache the size
14473         this.viewSize = [w, h];
14474         if(this.modal && this.mask.isVisible()){
14475             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14476             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14477         }
14478         if(this.isVisible()){
14479             this.constrainXY();
14480         }
14481     },
14482
14483     /**
14484      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14485      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14486      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14487      */
14488     destroy : function(removeEl){
14489         if(this.isVisible()){
14490             this.animateTarget = null;
14491             this.hide();
14492         }
14493         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14494         if(this.tabs){
14495             this.tabs.destroy(removeEl);
14496         }
14497         Roo.destroy(
14498              this.shim,
14499              this.proxy,
14500              this.resizer,
14501              this.close,
14502              this.mask
14503         );
14504         if(this.dd){
14505             this.dd.unreg();
14506         }
14507         if(this.buttons){
14508            for(var i = 0, len = this.buttons.length; i < len; i++){
14509                this.buttons[i].destroy();
14510            }
14511         }
14512         this.el.removeAllListeners();
14513         if(removeEl === true){
14514             this.el.update("");
14515             this.el.remove();
14516         }
14517         Roo.DialogManager.unregister(this);
14518     },
14519
14520     // private
14521     startMove : function(){
14522         if(this.proxyDrag){
14523             this.proxy.show();
14524         }
14525         if(this.constraintoviewport !== false){
14526             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14527         }
14528     },
14529
14530     // private
14531     endMove : function(){
14532         if(!this.proxyDrag){
14533             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14534         }else{
14535             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14536             this.proxy.hide();
14537         }
14538         this.refreshSize();
14539         this.adjustAssets();
14540         this.focus();
14541         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14542     },
14543
14544     /**
14545      * Brings this dialog to the front of any other visible dialogs
14546      * @return {Roo.BasicDialog} this
14547      */
14548     toFront : function(){
14549         Roo.DialogManager.bringToFront(this);
14550         return this;
14551     },
14552
14553     /**
14554      * Sends this dialog to the back (under) of any other visible dialogs
14555      * @return {Roo.BasicDialog} this
14556      */
14557     toBack : function(){
14558         Roo.DialogManager.sendToBack(this);
14559         return this;
14560     },
14561
14562     /**
14563      * Centers this dialog in the viewport
14564      * @return {Roo.BasicDialog} this
14565      */
14566     center : function(){
14567         var xy = this.el.getCenterXY(true);
14568         this.moveTo(xy[0], xy[1]);
14569         return this;
14570     },
14571
14572     /**
14573      * Moves the dialog's top-left corner to the specified point
14574      * @param {Number} x
14575      * @param {Number} y
14576      * @return {Roo.BasicDialog} this
14577      */
14578     moveTo : function(x, y){
14579         this.xy = [x,y];
14580         if(this.isVisible()){
14581             this.el.setXY(this.xy);
14582             this.adjustAssets();
14583         }
14584         return this;
14585     },
14586
14587     /**
14588      * Aligns the dialog to the specified element
14589      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14590      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14591      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14592      * @return {Roo.BasicDialog} this
14593      */
14594     alignTo : function(element, position, offsets){
14595         this.xy = this.el.getAlignToXY(element, position, offsets);
14596         if(this.isVisible()){
14597             this.el.setXY(this.xy);
14598             this.adjustAssets();
14599         }
14600         return this;
14601     },
14602
14603     /**
14604      * Anchors an element to another element and realigns it when the window is resized.
14605      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14606      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14607      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14608      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14609      * is a number, it is used as the buffer delay (defaults to 50ms).
14610      * @return {Roo.BasicDialog} this
14611      */
14612     anchorTo : function(el, alignment, offsets, monitorScroll){
14613         var action = function(){
14614             this.alignTo(el, alignment, offsets);
14615         };
14616         Roo.EventManager.onWindowResize(action, this);
14617         var tm = typeof monitorScroll;
14618         if(tm != 'undefined'){
14619             Roo.EventManager.on(window, 'scroll', action, this,
14620                 {buffer: tm == 'number' ? monitorScroll : 50});
14621         }
14622         action.call(this);
14623         return this;
14624     },
14625
14626     /**
14627      * Returns true if the dialog is visible
14628      * @return {Boolean}
14629      */
14630     isVisible : function(){
14631         return this.el.isVisible();
14632     },
14633
14634     // private
14635     animHide : function(callback){
14636         var b = Roo.get(this.animateTarget).getBox();
14637         this.proxy.show();
14638         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14639         this.el.hide();
14640         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14641                     this.hideEl.createDelegate(this, [callback]));
14642     },
14643
14644     /**
14645      * Hides the dialog.
14646      * @param {Function} callback (optional) Function to call when the dialog is hidden
14647      * @return {Roo.BasicDialog} this
14648      */
14649     hide : function(callback){
14650         if (this.fireEvent("beforehide", this) === false){
14651             return;
14652         }
14653         if(this.shadow){
14654             this.shadow.hide();
14655         }
14656         if(this.shim) {
14657           this.shim.hide();
14658         }
14659         // sometimes animateTarget seems to get set.. causing problems...
14660         // this just double checks..
14661         if(this.animateTarget && Roo.get(this.animateTarget)) {
14662            this.animHide(callback);
14663         }else{
14664             this.el.hide();
14665             this.hideEl(callback);
14666         }
14667         return this;
14668     },
14669
14670     // private
14671     hideEl : function(callback){
14672         this.proxy.hide();
14673         if(this.modal){
14674             this.mask.hide();
14675             Roo.get(document.body).removeClass("x-body-masked");
14676         }
14677         this.fireEvent("hide", this);
14678         if(typeof callback == "function"){
14679             callback();
14680         }
14681     },
14682
14683     // private
14684     hideAction : function(){
14685         this.setLeft("-10000px");
14686         this.setTop("-10000px");
14687         this.setStyle("visibility", "hidden");
14688     },
14689
14690     // private
14691     refreshSize : function(){
14692         this.size = this.el.getSize();
14693         this.xy = this.el.getXY();
14694         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14695     },
14696
14697     // private
14698     // z-index is managed by the DialogManager and may be overwritten at any time
14699     setZIndex : function(index){
14700         if(this.modal){
14701             this.mask.setStyle("z-index", index);
14702         }
14703         if(this.shim){
14704             this.shim.setStyle("z-index", ++index);
14705         }
14706         if(this.shadow){
14707             this.shadow.setZIndex(++index);
14708         }
14709         this.el.setStyle("z-index", ++index);
14710         if(this.proxy){
14711             this.proxy.setStyle("z-index", ++index);
14712         }
14713         if(this.resizer){
14714             this.resizer.proxy.setStyle("z-index", ++index);
14715         }
14716
14717         this.lastZIndex = index;
14718     },
14719
14720     /**
14721      * Returns the element for this dialog
14722      * @return {Roo.Element} The underlying dialog Element
14723      */
14724     getEl : function(){
14725         return this.el;
14726     }
14727 });
14728
14729 /**
14730  * @class Roo.DialogManager
14731  * Provides global access to BasicDialogs that have been created and
14732  * support for z-indexing (layering) multiple open dialogs.
14733  */
14734 Roo.DialogManager = function(){
14735     var list = {};
14736     var accessList = [];
14737     var front = null;
14738
14739     // private
14740     var sortDialogs = function(d1, d2){
14741         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14742     };
14743
14744     // private
14745     var orderDialogs = function(){
14746         accessList.sort(sortDialogs);
14747         var seed = Roo.DialogManager.zseed;
14748         for(var i = 0, len = accessList.length; i < len; i++){
14749             var dlg = accessList[i];
14750             if(dlg){
14751                 dlg.setZIndex(seed + (i*10));
14752             }
14753         }
14754     };
14755
14756     return {
14757         /**
14758          * The starting z-index for BasicDialogs (defaults to 9000)
14759          * @type Number The z-index value
14760          */
14761         zseed : 9000,
14762
14763         // private
14764         register : function(dlg){
14765             list[dlg.id] = dlg;
14766             accessList.push(dlg);
14767         },
14768
14769         // private
14770         unregister : function(dlg){
14771             delete list[dlg.id];
14772             var i=0;
14773             var len=0;
14774             if(!accessList.indexOf){
14775                 for(  i = 0, len = accessList.length; i < len; i++){
14776                     if(accessList[i] == dlg){
14777                         accessList.splice(i, 1);
14778                         return;
14779                     }
14780                 }
14781             }else{
14782                  i = accessList.indexOf(dlg);
14783                 if(i != -1){
14784                     accessList.splice(i, 1);
14785                 }
14786             }
14787         },
14788
14789         /**
14790          * Gets a registered dialog by id
14791          * @param {String/Object} id The id of the dialog or a dialog
14792          * @return {Roo.BasicDialog} this
14793          */
14794         get : function(id){
14795             return typeof id == "object" ? id : list[id];
14796         },
14797
14798         /**
14799          * Brings the specified dialog to the front
14800          * @param {String/Object} dlg The id of the dialog or a dialog
14801          * @return {Roo.BasicDialog} this
14802          */
14803         bringToFront : function(dlg){
14804             dlg = this.get(dlg);
14805             if(dlg != front){
14806                 front = dlg;
14807                 dlg._lastAccess = new Date().getTime();
14808                 orderDialogs();
14809             }
14810             return dlg;
14811         },
14812
14813         /**
14814          * Sends the specified dialog to the back
14815          * @param {String/Object} dlg The id of the dialog or a dialog
14816          * @return {Roo.BasicDialog} this
14817          */
14818         sendToBack : function(dlg){
14819             dlg = this.get(dlg);
14820             dlg._lastAccess = -(new Date().getTime());
14821             orderDialogs();
14822             return dlg;
14823         },
14824
14825         /**
14826          * Hides all dialogs
14827          */
14828         hideAll : function(){
14829             for(var id in list){
14830                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14831                     list[id].hide();
14832                 }
14833             }
14834         }
14835     };
14836 }();
14837
14838 /**
14839  * @class Roo.LayoutDialog
14840  * @extends Roo.BasicDialog
14841  * Dialog which provides adjustments for working with a layout in a Dialog.
14842  * Add your necessary layout config options to the dialog's config.<br>
14843  * Example usage (including a nested layout):
14844  * <pre><code>
14845 if(!dialog){
14846     dialog = new Roo.LayoutDialog("download-dlg", {
14847         modal: true,
14848         width:600,
14849         height:450,
14850         shadow:true,
14851         minWidth:500,
14852         minHeight:350,
14853         autoTabs:true,
14854         proxyDrag:true,
14855         // layout config merges with the dialog config
14856         center:{
14857             tabPosition: "top",
14858             alwaysShowTabs: true
14859         }
14860     });
14861     dialog.addKeyListener(27, dialog.hide, dialog);
14862     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14863     dialog.addButton("Build It!", this.getDownload, this);
14864
14865     // we can even add nested layouts
14866     var innerLayout = new Roo.BorderLayout("dl-inner", {
14867         east: {
14868             initialSize: 200,
14869             autoScroll:true,
14870             split:true
14871         },
14872         center: {
14873             autoScroll:true
14874         }
14875     });
14876     innerLayout.beginUpdate();
14877     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14878     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14879     innerLayout.endUpdate(true);
14880
14881     var layout = dialog.getLayout();
14882     layout.beginUpdate();
14883     layout.add("center", new Roo.ContentPanel("standard-panel",
14884                         {title: "Download the Source", fitToFrame:true}));
14885     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14886                {title: "Build your own roo.js"}));
14887     layout.getRegion("center").showPanel(sp);
14888     layout.endUpdate();
14889 }
14890 </code></pre>
14891     * @constructor
14892     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14893     * @param {Object} config configuration options
14894   */
14895 Roo.LayoutDialog = function(el, cfg){
14896     
14897     var config=  cfg;
14898     if (typeof(cfg) == 'undefined') {
14899         config = Roo.apply({}, el);
14900         // not sure why we use documentElement here.. - it should always be body.
14901         // IE7 borks horribly if we use documentElement.
14902         // webkit also does not like documentElement - it creates a body element...
14903         el = Roo.get( document.body || document.documentElement ).createChild();
14904         //config.autoCreate = true;
14905     }
14906     
14907     
14908     config.autoTabs = false;
14909     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14910     this.body.setStyle({overflow:"hidden", position:"relative"});
14911     this.layout = new Roo.BorderLayout(this.body.dom, config);
14912     this.layout.monitorWindowResize = false;
14913     this.el.addClass("x-dlg-auto-layout");
14914     // fix case when center region overwrites center function
14915     this.center = Roo.BasicDialog.prototype.center;
14916     this.on("show", this.layout.layout, this.layout, true);
14917     if (config.items) {
14918         var xitems = config.items;
14919         delete config.items;
14920         Roo.each(xitems, this.addxtype, this);
14921     }
14922     
14923     
14924 };
14925 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14926     /**
14927      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14928      * @deprecated
14929      */
14930     endUpdate : function(){
14931         this.layout.endUpdate();
14932     },
14933
14934     /**
14935      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14936      *  @deprecated
14937      */
14938     beginUpdate : function(){
14939         this.layout.beginUpdate();
14940     },
14941
14942     /**
14943      * Get the BorderLayout for this dialog
14944      * @return {Roo.BorderLayout}
14945      */
14946     getLayout : function(){
14947         return this.layout;
14948     },
14949
14950     showEl : function(){
14951         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14952         if(Roo.isIE7){
14953             this.layout.layout();
14954         }
14955     },
14956
14957     // private
14958     // Use the syncHeightBeforeShow config option to control this automatically
14959     syncBodyHeight : function(){
14960         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14961         if(this.layout){this.layout.layout();}
14962     },
14963     
14964       /**
14965      * Add an xtype element (actually adds to the layout.)
14966      * @return {Object} xdata xtype object data.
14967      */
14968     
14969     addxtype : function(c) {
14970         return this.layout.addxtype(c);
14971     }
14972 });/*
14973  * Based on:
14974  * Ext JS Library 1.1.1
14975  * Copyright(c) 2006-2007, Ext JS, LLC.
14976  *
14977  * Originally Released Under LGPL - original licence link has changed is not relivant.
14978  *
14979  * Fork - LGPL
14980  * <script type="text/javascript">
14981  */
14982  
14983 /**
14984  * @class Roo.MessageBox
14985  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14986  * Example usage:
14987  *<pre><code>
14988 // Basic alert:
14989 Roo.Msg.alert('Status', 'Changes saved successfully.');
14990
14991 // Prompt for user data:
14992 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14993     if (btn == 'ok'){
14994         // process text value...
14995     }
14996 });
14997
14998 // Show a dialog using config options:
14999 Roo.Msg.show({
15000    title:'Save Changes?',
15001    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15002    buttons: Roo.Msg.YESNOCANCEL,
15003    fn: processResult,
15004    animEl: 'elId'
15005 });
15006 </code></pre>
15007  * @singleton
15008  */
15009 Roo.MessageBox = function(){
15010     var dlg, opt, mask, waitTimer;
15011     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15012     var buttons, activeTextEl, bwidth;
15013
15014     // private
15015     var handleButton = function(button){
15016         dlg.hide();
15017         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15018     };
15019
15020     // private
15021     var handleHide = function(){
15022         if(opt && opt.cls){
15023             dlg.el.removeClass(opt.cls);
15024         }
15025         if(waitTimer){
15026             Roo.TaskMgr.stop(waitTimer);
15027             waitTimer = null;
15028         }
15029     };
15030
15031     // private
15032     var updateButtons = function(b){
15033         var width = 0;
15034         if(!b){
15035             buttons["ok"].hide();
15036             buttons["cancel"].hide();
15037             buttons["yes"].hide();
15038             buttons["no"].hide();
15039             dlg.footer.dom.style.display = 'none';
15040             return width;
15041         }
15042         dlg.footer.dom.style.display = '';
15043         for(var k in buttons){
15044             if(typeof buttons[k] != "function"){
15045                 if(b[k]){
15046                     buttons[k].show();
15047                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15048                     width += buttons[k].el.getWidth()+15;
15049                 }else{
15050                     buttons[k].hide();
15051                 }
15052             }
15053         }
15054         return width;
15055     };
15056
15057     // private
15058     var handleEsc = function(d, k, e){
15059         if(opt && opt.closable !== false){
15060             dlg.hide();
15061         }
15062         if(e){
15063             e.stopEvent();
15064         }
15065     };
15066
15067     return {
15068         /**
15069          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15070          * @return {Roo.BasicDialog} The BasicDialog element
15071          */
15072         getDialog : function(){
15073            if(!dlg){
15074                 dlg = new Roo.BasicDialog("x-msg-box", {
15075                     autoCreate : true,
15076                     shadow: true,
15077                     draggable: true,
15078                     resizable:false,
15079                     constraintoviewport:false,
15080                     fixedcenter:true,
15081                     collapsible : false,
15082                     shim:true,
15083                     modal: true,
15084                     width:400, height:100,
15085                     buttonAlign:"center",
15086                     closeClick : function(){
15087                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15088                             handleButton("no");
15089                         }else{
15090                             handleButton("cancel");
15091                         }
15092                     }
15093                 });
15094                 dlg.on("hide", handleHide);
15095                 mask = dlg.mask;
15096                 dlg.addKeyListener(27, handleEsc);
15097                 buttons = {};
15098                 var bt = this.buttonText;
15099                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15100                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15101                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15102                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15103                 bodyEl = dlg.body.createChild({
15104
15105                     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>'
15106                 });
15107                 msgEl = bodyEl.dom.firstChild;
15108                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15109                 textboxEl.enableDisplayMode();
15110                 textboxEl.addKeyListener([10,13], function(){
15111                     if(dlg.isVisible() && opt && opt.buttons){
15112                         if(opt.buttons.ok){
15113                             handleButton("ok");
15114                         }else if(opt.buttons.yes){
15115                             handleButton("yes");
15116                         }
15117                     }
15118                 });
15119                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15120                 textareaEl.enableDisplayMode();
15121                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15122                 progressEl.enableDisplayMode();
15123                 var pf = progressEl.dom.firstChild;
15124                 if (pf) {
15125                     pp = Roo.get(pf.firstChild);
15126                     pp.setHeight(pf.offsetHeight);
15127                 }
15128                 
15129             }
15130             return dlg;
15131         },
15132
15133         /**
15134          * Updates the message box body text
15135          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15136          * the XHTML-compliant non-breaking space character '&amp;#160;')
15137          * @return {Roo.MessageBox} This message box
15138          */
15139         updateText : function(text){
15140             if(!dlg.isVisible() && !opt.width){
15141                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15142             }
15143             msgEl.innerHTML = text || '&#160;';
15144       
15145             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15146             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15147             var w = Math.max(
15148                     Math.min(opt.width || cw , this.maxWidth), 
15149                     Math.max(opt.minWidth || this.minWidth, bwidth)
15150             );
15151             if(opt.prompt){
15152                 activeTextEl.setWidth(w);
15153             }
15154             if(dlg.isVisible()){
15155                 dlg.fixedcenter = false;
15156             }
15157             // to big, make it scroll. = But as usual stupid IE does not support
15158             // !important..
15159             
15160             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15161                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15162                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15163             } else {
15164                 bodyEl.dom.style.height = '';
15165                 bodyEl.dom.style.overflowY = '';
15166             }
15167             if (cw > w) {
15168                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15169             } else {
15170                 bodyEl.dom.style.overflowX = '';
15171             }
15172             
15173             dlg.setContentSize(w, bodyEl.getHeight());
15174             if(dlg.isVisible()){
15175                 dlg.fixedcenter = true;
15176             }
15177             return this;
15178         },
15179
15180         /**
15181          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15182          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15183          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15184          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15185          * @return {Roo.MessageBox} This message box
15186          */
15187         updateProgress : function(value, text){
15188             if(text){
15189                 this.updateText(text);
15190             }
15191             if (pp) { // weird bug on my firefox - for some reason this is not defined
15192                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15193             }
15194             return this;
15195         },        
15196
15197         /**
15198          * Returns true if the message box is currently displayed
15199          * @return {Boolean} True if the message box is visible, else false
15200          */
15201         isVisible : function(){
15202             return dlg && dlg.isVisible();  
15203         },
15204
15205         /**
15206          * Hides the message box if it is displayed
15207          */
15208         hide : function(){
15209             if(this.isVisible()){
15210                 dlg.hide();
15211             }  
15212         },
15213
15214         /**
15215          * Displays a new message box, or reinitializes an existing message box, based on the config options
15216          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15217          * The following config object properties are supported:
15218          * <pre>
15219 Property    Type             Description
15220 ----------  ---------------  ------------------------------------------------------------------------------------
15221 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15222                                    closes (defaults to undefined)
15223 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15224                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15225 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15226                                    progress and wait dialogs will ignore this property and always hide the
15227                                    close button as they can only be closed programmatically.
15228 cls               String           A custom CSS class to apply to the message box element
15229 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15230                                    displayed (defaults to 75)
15231 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15232                                    function will be btn (the name of the button that was clicked, if applicable,
15233                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15234                                    Progress and wait dialogs will ignore this option since they do not respond to
15235                                    user actions and can only be closed programmatically, so any required function
15236                                    should be called by the same code after it closes the dialog.
15237 icon              String           A CSS class that provides a background image to be used as an icon for
15238                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15239 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15240 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15241 modal             Boolean          False to allow user interaction with the page while the message box is
15242                                    displayed (defaults to true)
15243 msg               String           A string that will replace the existing message box body text (defaults
15244                                    to the XHTML-compliant non-breaking space character '&#160;')
15245 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15246 progress          Boolean          True to display a progress bar (defaults to false)
15247 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15248 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15249 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15250 title             String           The title text
15251 value             String           The string value to set into the active textbox element if displayed
15252 wait              Boolean          True to display a progress bar (defaults to false)
15253 width             Number           The width of the dialog in pixels
15254 </pre>
15255          *
15256          * Example usage:
15257          * <pre><code>
15258 Roo.Msg.show({
15259    title: 'Address',
15260    msg: 'Please enter your address:',
15261    width: 300,
15262    buttons: Roo.MessageBox.OKCANCEL,
15263    multiline: true,
15264    fn: saveAddress,
15265    animEl: 'addAddressBtn'
15266 });
15267 </code></pre>
15268          * @param {Object} config Configuration options
15269          * @return {Roo.MessageBox} This message box
15270          */
15271         show : function(options)
15272         {
15273             
15274             // this causes nightmares if you show one dialog after another
15275             // especially on callbacks..
15276              
15277             if(this.isVisible()){
15278                 
15279                 this.hide();
15280                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15281                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15282                 Roo.log("New Dialog Message:" +  options.msg )
15283                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15284                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15285                 
15286             }
15287             var d = this.getDialog();
15288             opt = options;
15289             d.setTitle(opt.title || "&#160;");
15290             d.close.setDisplayed(opt.closable !== false);
15291             activeTextEl = textboxEl;
15292             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15293             if(opt.prompt){
15294                 if(opt.multiline){
15295                     textboxEl.hide();
15296                     textareaEl.show();
15297                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15298                         opt.multiline : this.defaultTextHeight);
15299                     activeTextEl = textareaEl;
15300                 }else{
15301                     textboxEl.show();
15302                     textareaEl.hide();
15303                 }
15304             }else{
15305                 textboxEl.hide();
15306                 textareaEl.hide();
15307             }
15308             progressEl.setDisplayed(opt.progress === true);
15309             this.updateProgress(0);
15310             activeTextEl.dom.value = opt.value || "";
15311             if(opt.prompt){
15312                 dlg.setDefaultButton(activeTextEl);
15313             }else{
15314                 var bs = opt.buttons;
15315                 var db = null;
15316                 if(bs && bs.ok){
15317                     db = buttons["ok"];
15318                 }else if(bs && bs.yes){
15319                     db = buttons["yes"];
15320                 }
15321                 dlg.setDefaultButton(db);
15322             }
15323             bwidth = updateButtons(opt.buttons);
15324             this.updateText(opt.msg);
15325             if(opt.cls){
15326                 d.el.addClass(opt.cls);
15327             }
15328             d.proxyDrag = opt.proxyDrag === true;
15329             d.modal = opt.modal !== false;
15330             d.mask = opt.modal !== false ? mask : false;
15331             if(!d.isVisible()){
15332                 // force it to the end of the z-index stack so it gets a cursor in FF
15333                 document.body.appendChild(dlg.el.dom);
15334                 d.animateTarget = null;
15335                 d.show(options.animEl);
15336             }
15337             return this;
15338         },
15339
15340         /**
15341          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15342          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15343          * and closing the message box when the process is complete.
15344          * @param {String} title The title bar text
15345          * @param {String} msg The message box body text
15346          * @return {Roo.MessageBox} This message box
15347          */
15348         progress : function(title, msg){
15349             this.show({
15350                 title : title,
15351                 msg : msg,
15352                 buttons: false,
15353                 progress:true,
15354                 closable:false,
15355                 minWidth: this.minProgressWidth,
15356                 modal : true
15357             });
15358             return this;
15359         },
15360
15361         /**
15362          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15363          * If a callback function is passed it will be called after the user clicks the button, and the
15364          * id of the button that was clicked will be passed as the only parameter to the callback
15365          * (could also be the top-right close button).
15366          * @param {String} title The title bar text
15367          * @param {String} msg The message box body text
15368          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15369          * @param {Object} scope (optional) The scope of the callback function
15370          * @return {Roo.MessageBox} This message box
15371          */
15372         alert : function(title, msg, fn, scope){
15373             this.show({
15374                 title : title,
15375                 msg : msg,
15376                 buttons: this.OK,
15377                 fn: fn,
15378                 scope : scope,
15379                 modal : true
15380             });
15381             return this;
15382         },
15383
15384         /**
15385          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15386          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15387          * You are responsible for closing the message box when the process is complete.
15388          * @param {String} msg The message box body text
15389          * @param {String} title (optional) The title bar text
15390          * @return {Roo.MessageBox} This message box
15391          */
15392         wait : function(msg, title){
15393             this.show({
15394                 title : title,
15395                 msg : msg,
15396                 buttons: false,
15397                 closable:false,
15398                 progress:true,
15399                 modal:true,
15400                 width:300,
15401                 wait:true
15402             });
15403             waitTimer = Roo.TaskMgr.start({
15404                 run: function(i){
15405                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15406                 },
15407                 interval: 1000
15408             });
15409             return this;
15410         },
15411
15412         /**
15413          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15414          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15415          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15416          * @param {String} title The title bar text
15417          * @param {String} msg The message box body text
15418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15419          * @param {Object} scope (optional) The scope of the callback function
15420          * @return {Roo.MessageBox} This message box
15421          */
15422         confirm : function(title, msg, fn, scope){
15423             this.show({
15424                 title : title,
15425                 msg : msg,
15426                 buttons: this.YESNO,
15427                 fn: fn,
15428                 scope : scope,
15429                 modal : true
15430             });
15431             return this;
15432         },
15433
15434         /**
15435          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15436          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15437          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15438          * (could also be the top-right close button) and the text that was entered will be passed as the two
15439          * parameters to the callback.
15440          * @param {String} title The title bar text
15441          * @param {String} msg The message box body text
15442          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15443          * @param {Object} scope (optional) The scope of the callback function
15444          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15445          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15446          * @return {Roo.MessageBox} This message box
15447          */
15448         prompt : function(title, msg, fn, scope, multiline){
15449             this.show({
15450                 title : title,
15451                 msg : msg,
15452                 buttons: this.OKCANCEL,
15453                 fn: fn,
15454                 minWidth:250,
15455                 scope : scope,
15456                 prompt:true,
15457                 multiline: multiline,
15458                 modal : true
15459             });
15460             return this;
15461         },
15462
15463         /**
15464          * Button config that displays a single OK button
15465          * @type Object
15466          */
15467         OK : {ok:true},
15468         /**
15469          * Button config that displays Yes and No buttons
15470          * @type Object
15471          */
15472         YESNO : {yes:true, no:true},
15473         /**
15474          * Button config that displays OK and Cancel buttons
15475          * @type Object
15476          */
15477         OKCANCEL : {ok:true, cancel:true},
15478         /**
15479          * Button config that displays Yes, No and Cancel buttons
15480          * @type Object
15481          */
15482         YESNOCANCEL : {yes:true, no:true, cancel:true},
15483
15484         /**
15485          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15486          * @type Number
15487          */
15488         defaultTextHeight : 75,
15489         /**
15490          * The maximum width in pixels of the message box (defaults to 600)
15491          * @type Number
15492          */
15493         maxWidth : 600,
15494         /**
15495          * The minimum width in pixels of the message box (defaults to 100)
15496          * @type Number
15497          */
15498         minWidth : 100,
15499         /**
15500          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15501          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15502          * @type Number
15503          */
15504         minProgressWidth : 250,
15505         /**
15506          * An object containing the default button text strings that can be overriden for localized language support.
15507          * Supported properties are: ok, cancel, yes and no.
15508          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15509          * @type Object
15510          */
15511         buttonText : {
15512             ok : "OK",
15513             cancel : "Cancel",
15514             yes : "Yes",
15515             no : "No"
15516         }
15517     };
15518 }();
15519
15520 /**
15521  * Shorthand for {@link Roo.MessageBox}
15522  */
15523 Roo.Msg = Roo.MessageBox;/*
15524  * Based on:
15525  * Ext JS Library 1.1.1
15526  * Copyright(c) 2006-2007, Ext JS, LLC.
15527  *
15528  * Originally Released Under LGPL - original licence link has changed is not relivant.
15529  *
15530  * Fork - LGPL
15531  * <script type="text/javascript">
15532  */
15533 /**
15534  * @class Roo.QuickTips
15535  * Provides attractive and customizable tooltips for any element.
15536  * @singleton
15537  */
15538 Roo.QuickTips = function(){
15539     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15540     var ce, bd, xy, dd;
15541     var visible = false, disabled = true, inited = false;
15542     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15543     
15544     var onOver = function(e){
15545         if(disabled){
15546             return;
15547         }
15548         var t = e.getTarget();
15549         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15550             return;
15551         }
15552         if(ce && t == ce.el){
15553             clearTimeout(hideProc);
15554             return;
15555         }
15556         if(t && tagEls[t.id]){
15557             tagEls[t.id].el = t;
15558             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15559             return;
15560         }
15561         var ttp, et = Roo.fly(t);
15562         var ns = cfg.namespace;
15563         if(tm.interceptTitles && t.title){
15564             ttp = t.title;
15565             t.qtip = ttp;
15566             t.removeAttribute("title");
15567             e.preventDefault();
15568         }else{
15569             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15570         }
15571         if(ttp){
15572             showProc = show.defer(tm.showDelay, tm, [{
15573                 el: t, 
15574                 text: ttp, 
15575                 width: et.getAttributeNS(ns, cfg.width),
15576                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15577                 title: et.getAttributeNS(ns, cfg.title),
15578                     cls: et.getAttributeNS(ns, cfg.cls)
15579             }]);
15580         }
15581     };
15582     
15583     var onOut = function(e){
15584         clearTimeout(showProc);
15585         var t = e.getTarget();
15586         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15587             hideProc = setTimeout(hide, tm.hideDelay);
15588         }
15589     };
15590     
15591     var onMove = function(e){
15592         if(disabled){
15593             return;
15594         }
15595         xy = e.getXY();
15596         xy[1] += 18;
15597         if(tm.trackMouse && ce){
15598             el.setXY(xy);
15599         }
15600     };
15601     
15602     var onDown = function(e){
15603         clearTimeout(showProc);
15604         clearTimeout(hideProc);
15605         if(!e.within(el)){
15606             if(tm.hideOnClick){
15607                 hide();
15608                 tm.disable();
15609                 tm.enable.defer(100, tm);
15610             }
15611         }
15612     };
15613     
15614     var getPad = function(){
15615         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15616     };
15617
15618     var show = function(o){
15619         if(disabled){
15620             return;
15621         }
15622         clearTimeout(dismissProc);
15623         ce = o;
15624         if(removeCls){ // in case manually hidden
15625             el.removeClass(removeCls);
15626             removeCls = null;
15627         }
15628         if(ce.cls){
15629             el.addClass(ce.cls);
15630             removeCls = ce.cls;
15631         }
15632         if(ce.title){
15633             tipTitle.update(ce.title);
15634             tipTitle.show();
15635         }else{
15636             tipTitle.update('');
15637             tipTitle.hide();
15638         }
15639         el.dom.style.width  = tm.maxWidth+'px';
15640         //tipBody.dom.style.width = '';
15641         tipBodyText.update(o.text);
15642         var p = getPad(), w = ce.width;
15643         if(!w){
15644             var td = tipBodyText.dom;
15645             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15646             if(aw > tm.maxWidth){
15647                 w = tm.maxWidth;
15648             }else if(aw < tm.minWidth){
15649                 w = tm.minWidth;
15650             }else{
15651                 w = aw;
15652             }
15653         }
15654         //tipBody.setWidth(w);
15655         el.setWidth(parseInt(w, 10) + p);
15656         if(ce.autoHide === false){
15657             close.setDisplayed(true);
15658             if(dd){
15659                 dd.unlock();
15660             }
15661         }else{
15662             close.setDisplayed(false);
15663             if(dd){
15664                 dd.lock();
15665             }
15666         }
15667         if(xy){
15668             el.avoidY = xy[1]-18;
15669             el.setXY(xy);
15670         }
15671         if(tm.animate){
15672             el.setOpacity(.1);
15673             el.setStyle("visibility", "visible");
15674             el.fadeIn({callback: afterShow});
15675         }else{
15676             afterShow();
15677         }
15678     };
15679     
15680     var afterShow = function(){
15681         if(ce){
15682             el.show();
15683             esc.enable();
15684             if(tm.autoDismiss && ce.autoHide !== false){
15685                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15686             }
15687         }
15688     };
15689     
15690     var hide = function(noanim){
15691         clearTimeout(dismissProc);
15692         clearTimeout(hideProc);
15693         ce = null;
15694         if(el.isVisible()){
15695             esc.disable();
15696             if(noanim !== true && tm.animate){
15697                 el.fadeOut({callback: afterHide});
15698             }else{
15699                 afterHide();
15700             } 
15701         }
15702     };
15703     
15704     var afterHide = function(){
15705         el.hide();
15706         if(removeCls){
15707             el.removeClass(removeCls);
15708             removeCls = null;
15709         }
15710     };
15711     
15712     return {
15713         /**
15714         * @cfg {Number} minWidth
15715         * The minimum width of the quick tip (defaults to 40)
15716         */
15717        minWidth : 40,
15718         /**
15719         * @cfg {Number} maxWidth
15720         * The maximum width of the quick tip (defaults to 300)
15721         */
15722        maxWidth : 300,
15723         /**
15724         * @cfg {Boolean} interceptTitles
15725         * True to automatically use the element's DOM title value if available (defaults to false)
15726         */
15727        interceptTitles : false,
15728         /**
15729         * @cfg {Boolean} trackMouse
15730         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15731         */
15732        trackMouse : false,
15733         /**
15734         * @cfg {Boolean} hideOnClick
15735         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15736         */
15737        hideOnClick : true,
15738         /**
15739         * @cfg {Number} showDelay
15740         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15741         */
15742        showDelay : 500,
15743         /**
15744         * @cfg {Number} hideDelay
15745         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15746         */
15747        hideDelay : 200,
15748         /**
15749         * @cfg {Boolean} autoHide
15750         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15751         * Used in conjunction with hideDelay.
15752         */
15753        autoHide : true,
15754         /**
15755         * @cfg {Boolean}
15756         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15757         * (defaults to true).  Used in conjunction with autoDismissDelay.
15758         */
15759        autoDismiss : true,
15760         /**
15761         * @cfg {Number}
15762         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15763         */
15764        autoDismissDelay : 5000,
15765        /**
15766         * @cfg {Boolean} animate
15767         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15768         */
15769        animate : false,
15770
15771        /**
15772         * @cfg {String} title
15773         * Title text to display (defaults to '').  This can be any valid HTML markup.
15774         */
15775         title: '',
15776        /**
15777         * @cfg {String} text
15778         * Body text to display (defaults to '').  This can be any valid HTML markup.
15779         */
15780         text : '',
15781        /**
15782         * @cfg {String} cls
15783         * A CSS class to apply to the base quick tip element (defaults to '').
15784         */
15785         cls : '',
15786        /**
15787         * @cfg {Number} width
15788         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15789         * minWidth or maxWidth.
15790         */
15791         width : null,
15792
15793     /**
15794      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15795      * or display QuickTips in a page.
15796      */
15797        init : function(){
15798           tm = Roo.QuickTips;
15799           cfg = tm.tagConfig;
15800           if(!inited){
15801               if(!Roo.isReady){ // allow calling of init() before onReady
15802                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15803                   return;
15804               }
15805               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15806               el.fxDefaults = {stopFx: true};
15807               // maximum custom styling
15808               //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>');
15809               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>');              
15810               tipTitle = el.child('h3');
15811               tipTitle.enableDisplayMode("block");
15812               tipBody = el.child('div.x-tip-bd');
15813               tipBodyText = el.child('div.x-tip-bd-inner');
15814               //bdLeft = el.child('div.x-tip-bd-left');
15815               //bdRight = el.child('div.x-tip-bd-right');
15816               close = el.child('div.x-tip-close');
15817               close.enableDisplayMode("block");
15818               close.on("click", hide);
15819               var d = Roo.get(document);
15820               d.on("mousedown", onDown);
15821               d.on("mouseover", onOver);
15822               d.on("mouseout", onOut);
15823               d.on("mousemove", onMove);
15824               esc = d.addKeyListener(27, hide);
15825               esc.disable();
15826               if(Roo.dd.DD){
15827                   dd = el.initDD("default", null, {
15828                       onDrag : function(){
15829                           el.sync();  
15830                       }
15831                   });
15832                   dd.setHandleElId(tipTitle.id);
15833                   dd.lock();
15834               }
15835               inited = true;
15836           }
15837           this.enable(); 
15838        },
15839
15840     /**
15841      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15842      * are supported:
15843      * <pre>
15844 Property    Type                   Description
15845 ----------  ---------------------  ------------------------------------------------------------------------
15846 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15847      * </ul>
15848      * @param {Object} config The config object
15849      */
15850        register : function(config){
15851            var cs = config instanceof Array ? config : arguments;
15852            for(var i = 0, len = cs.length; i < len; i++) {
15853                var c = cs[i];
15854                var target = c.target;
15855                if(target){
15856                    if(target instanceof Array){
15857                        for(var j = 0, jlen = target.length; j < jlen; j++){
15858                            tagEls[target[j]] = c;
15859                        }
15860                    }else{
15861                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15862                    }
15863                }
15864            }
15865        },
15866
15867     /**
15868      * Removes this quick tip from its element and destroys it.
15869      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15870      */
15871        unregister : function(el){
15872            delete tagEls[Roo.id(el)];
15873        },
15874
15875     /**
15876      * Enable this quick tip.
15877      */
15878        enable : function(){
15879            if(inited && disabled){
15880                locks.pop();
15881                if(locks.length < 1){
15882                    disabled = false;
15883                }
15884            }
15885        },
15886
15887     /**
15888      * Disable this quick tip.
15889      */
15890        disable : function(){
15891           disabled = true;
15892           clearTimeout(showProc);
15893           clearTimeout(hideProc);
15894           clearTimeout(dismissProc);
15895           if(ce){
15896               hide(true);
15897           }
15898           locks.push(1);
15899        },
15900
15901     /**
15902      * Returns true if the quick tip is enabled, else false.
15903      */
15904        isEnabled : function(){
15905             return !disabled;
15906        },
15907
15908         // private
15909        tagConfig : {
15910            namespace : "ext",
15911            attribute : "qtip",
15912            width : "width",
15913            target : "target",
15914            title : "qtitle",
15915            hide : "hide",
15916            cls : "qclass"
15917        }
15918    };
15919 }();
15920
15921 // backwards compat
15922 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15923  * Based on:
15924  * Ext JS Library 1.1.1
15925  * Copyright(c) 2006-2007, Ext JS, LLC.
15926  *
15927  * Originally Released Under LGPL - original licence link has changed is not relivant.
15928  *
15929  * Fork - LGPL
15930  * <script type="text/javascript">
15931  */
15932  
15933
15934 /**
15935  * @class Roo.tree.TreePanel
15936  * @extends Roo.data.Tree
15937
15938  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15939  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15940  * @cfg {Boolean} enableDD true to enable drag and drop
15941  * @cfg {Boolean} enableDrag true to enable just drag
15942  * @cfg {Boolean} enableDrop true to enable just drop
15943  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15944  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15945  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15946  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15947  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15948  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15949  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15950  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15951  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15952  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15953  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15954  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15955  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15956  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15957  * @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>
15958  * @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>
15959  * 
15960  * @constructor
15961  * @param {String/HTMLElement/Element} el The container element
15962  * @param {Object} config
15963  */
15964 Roo.tree.TreePanel = function(el, config){
15965     var root = false;
15966     var loader = false;
15967     if (config.root) {
15968         root = config.root;
15969         delete config.root;
15970     }
15971     if (config.loader) {
15972         loader = config.loader;
15973         delete config.loader;
15974     }
15975     
15976     Roo.apply(this, config);
15977     Roo.tree.TreePanel.superclass.constructor.call(this);
15978     this.el = Roo.get(el);
15979     this.el.addClass('x-tree');
15980     //console.log(root);
15981     if (root) {
15982         this.setRootNode( Roo.factory(root, Roo.tree));
15983     }
15984     if (loader) {
15985         this.loader = Roo.factory(loader, Roo.tree);
15986     }
15987    /**
15988     * Read-only. The id of the container element becomes this TreePanel's id.
15989     */
15990     this.id = this.el.id;
15991     this.addEvents({
15992         /**
15993         * @event beforeload
15994         * Fires before a node is loaded, return false to cancel
15995         * @param {Node} node The node being loaded
15996         */
15997         "beforeload" : true,
15998         /**
15999         * @event load
16000         * Fires when a node is loaded
16001         * @param {Node} node The node that was loaded
16002         */
16003         "load" : true,
16004         /**
16005         * @event textchange
16006         * Fires when the text for a node is changed
16007         * @param {Node} node The node
16008         * @param {String} text The new text
16009         * @param {String} oldText The old text
16010         */
16011         "textchange" : true,
16012         /**
16013         * @event beforeexpand
16014         * Fires before a node is expanded, return false to cancel.
16015         * @param {Node} node The node
16016         * @param {Boolean} deep
16017         * @param {Boolean} anim
16018         */
16019         "beforeexpand" : true,
16020         /**
16021         * @event beforecollapse
16022         * Fires before a node is collapsed, return false to cancel.
16023         * @param {Node} node The node
16024         * @param {Boolean} deep
16025         * @param {Boolean} anim
16026         */
16027         "beforecollapse" : true,
16028         /**
16029         * @event expand
16030         * Fires when a node is expanded
16031         * @param {Node} node The node
16032         */
16033         "expand" : true,
16034         /**
16035         * @event disabledchange
16036         * Fires when the disabled status of a node changes
16037         * @param {Node} node The node
16038         * @param {Boolean} disabled
16039         */
16040         "disabledchange" : true,
16041         /**
16042         * @event collapse
16043         * Fires when a node is collapsed
16044         * @param {Node} node The node
16045         */
16046         "collapse" : true,
16047         /**
16048         * @event beforeclick
16049         * Fires before click processing on a node. Return false to cancel the default action.
16050         * @param {Node} node The node
16051         * @param {Roo.EventObject} e The event object
16052         */
16053         "beforeclick":true,
16054         /**
16055         * @event checkchange
16056         * Fires when a node with a checkbox's checked property changes
16057         * @param {Node} this This node
16058         * @param {Boolean} checked
16059         */
16060         "checkchange":true,
16061         /**
16062         * @event click
16063         * Fires when a node is clicked
16064         * @param {Node} node The node
16065         * @param {Roo.EventObject} e The event object
16066         */
16067         "click":true,
16068         /**
16069         * @event dblclick
16070         * Fires when a node is double clicked
16071         * @param {Node} node The node
16072         * @param {Roo.EventObject} e The event object
16073         */
16074         "dblclick":true,
16075         /**
16076         * @event contextmenu
16077         * Fires when a node is right clicked
16078         * @param {Node} node The node
16079         * @param {Roo.EventObject} e The event object
16080         */
16081         "contextmenu":true,
16082         /**
16083         * @event beforechildrenrendered
16084         * Fires right before the child nodes for a node are rendered
16085         * @param {Node} node The node
16086         */
16087         "beforechildrenrendered":true,
16088         /**
16089         * @event startdrag
16090         * Fires when a node starts being dragged
16091         * @param {Roo.tree.TreePanel} this
16092         * @param {Roo.tree.TreeNode} node
16093         * @param {event} e The raw browser event
16094         */ 
16095        "startdrag" : true,
16096        /**
16097         * @event enddrag
16098         * Fires when a drag operation is complete
16099         * @param {Roo.tree.TreePanel} this
16100         * @param {Roo.tree.TreeNode} node
16101         * @param {event} e The raw browser event
16102         */
16103        "enddrag" : true,
16104        /**
16105         * @event dragdrop
16106         * Fires when a dragged node is dropped on a valid DD target
16107         * @param {Roo.tree.TreePanel} this
16108         * @param {Roo.tree.TreeNode} node
16109         * @param {DD} dd The dd it was dropped on
16110         * @param {event} e The raw browser event
16111         */
16112        "dragdrop" : true,
16113        /**
16114         * @event beforenodedrop
16115         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16116         * passed to handlers has the following properties:<br />
16117         * <ul style="padding:5px;padding-left:16px;">
16118         * <li>tree - The TreePanel</li>
16119         * <li>target - The node being targeted for the drop</li>
16120         * <li>data - The drag data from the drag source</li>
16121         * <li>point - The point of the drop - append, above or below</li>
16122         * <li>source - The drag source</li>
16123         * <li>rawEvent - Raw mouse event</li>
16124         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16125         * to be inserted by setting them on this object.</li>
16126         * <li>cancel - Set this to true to cancel the drop.</li>
16127         * </ul>
16128         * @param {Object} dropEvent
16129         */
16130        "beforenodedrop" : true,
16131        /**
16132         * @event nodedrop
16133         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16134         * passed to handlers has the following properties:<br />
16135         * <ul style="padding:5px;padding-left:16px;">
16136         * <li>tree - The TreePanel</li>
16137         * <li>target - The node being targeted for the drop</li>
16138         * <li>data - The drag data from the drag source</li>
16139         * <li>point - The point of the drop - append, above or below</li>
16140         * <li>source - The drag source</li>
16141         * <li>rawEvent - Raw mouse event</li>
16142         * <li>dropNode - Dropped node(s).</li>
16143         * </ul>
16144         * @param {Object} dropEvent
16145         */
16146        "nodedrop" : true,
16147         /**
16148         * @event nodedragover
16149         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16150         * passed to handlers has the following properties:<br />
16151         * <ul style="padding:5px;padding-left:16px;">
16152         * <li>tree - The TreePanel</li>
16153         * <li>target - The node being targeted for the drop</li>
16154         * <li>data - The drag data from the drag source</li>
16155         * <li>point - The point of the drop - append, above or below</li>
16156         * <li>source - The drag source</li>
16157         * <li>rawEvent - Raw mouse event</li>
16158         * <li>dropNode - Drop node(s) provided by the source.</li>
16159         * <li>cancel - Set this to true to signal drop not allowed.</li>
16160         * </ul>
16161         * @param {Object} dragOverEvent
16162         */
16163        "nodedragover" : true
16164         
16165     });
16166     if(this.singleExpand){
16167        this.on("beforeexpand", this.restrictExpand, this);
16168     }
16169     if (this.editor) {
16170         this.editor.tree = this;
16171         this.editor = Roo.factory(this.editor, Roo.tree);
16172     }
16173     
16174     if (this.selModel) {
16175         this.selModel = Roo.factory(this.selModel, Roo.tree);
16176     }
16177    
16178 };
16179 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16180     rootVisible : true,
16181     animate: Roo.enableFx,
16182     lines : true,
16183     enableDD : false,
16184     hlDrop : Roo.enableFx,
16185   
16186     renderer: false,
16187     
16188     rendererTip: false,
16189     // private
16190     restrictExpand : function(node){
16191         var p = node.parentNode;
16192         if(p){
16193             if(p.expandedChild && p.expandedChild.parentNode == p){
16194                 p.expandedChild.collapse();
16195             }
16196             p.expandedChild = node;
16197         }
16198     },
16199
16200     // private override
16201     setRootNode : function(node){
16202         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16203         if(!this.rootVisible){
16204             node.ui = new Roo.tree.RootTreeNodeUI(node);
16205         }
16206         return node;
16207     },
16208
16209     /**
16210      * Returns the container element for this TreePanel
16211      */
16212     getEl : function(){
16213         return this.el;
16214     },
16215
16216     /**
16217      * Returns the default TreeLoader for this TreePanel
16218      */
16219     getLoader : function(){
16220         return this.loader;
16221     },
16222
16223     /**
16224      * Expand all nodes
16225      */
16226     expandAll : function(){
16227         this.root.expand(true);
16228     },
16229
16230     /**
16231      * Collapse all nodes
16232      */
16233     collapseAll : function(){
16234         this.root.collapse(true);
16235     },
16236
16237     /**
16238      * Returns the selection model used by this TreePanel
16239      */
16240     getSelectionModel : function(){
16241         if(!this.selModel){
16242             this.selModel = new Roo.tree.DefaultSelectionModel();
16243         }
16244         return this.selModel;
16245     },
16246
16247     /**
16248      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16249      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16250      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16251      * @return {Array}
16252      */
16253     getChecked : function(a, startNode){
16254         startNode = startNode || this.root;
16255         var r = [];
16256         var f = function(){
16257             if(this.attributes.checked){
16258                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16259             }
16260         }
16261         startNode.cascade(f);
16262         return r;
16263     },
16264
16265     /**
16266      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16267      * @param {String} path
16268      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16269      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16270      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16271      */
16272     expandPath : function(path, attr, callback){
16273         attr = attr || "id";
16274         var keys = path.split(this.pathSeparator);
16275         var curNode = this.root;
16276         if(curNode.attributes[attr] != keys[1]){ // invalid root
16277             if(callback){
16278                 callback(false, null);
16279             }
16280             return;
16281         }
16282         var index = 1;
16283         var f = function(){
16284             if(++index == keys.length){
16285                 if(callback){
16286                     callback(true, curNode);
16287                 }
16288                 return;
16289             }
16290             var c = curNode.findChild(attr, keys[index]);
16291             if(!c){
16292                 if(callback){
16293                     callback(false, curNode);
16294                 }
16295                 return;
16296             }
16297             curNode = c;
16298             c.expand(false, false, f);
16299         };
16300         curNode.expand(false, false, f);
16301     },
16302
16303     /**
16304      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16305      * @param {String} path
16306      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16307      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16308      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16309      */
16310     selectPath : function(path, attr, callback){
16311         attr = attr || "id";
16312         var keys = path.split(this.pathSeparator);
16313         var v = keys.pop();
16314         if(keys.length > 0){
16315             var f = function(success, node){
16316                 if(success && node){
16317                     var n = node.findChild(attr, v);
16318                     if(n){
16319                         n.select();
16320                         if(callback){
16321                             callback(true, n);
16322                         }
16323                     }else if(callback){
16324                         callback(false, n);
16325                     }
16326                 }else{
16327                     if(callback){
16328                         callback(false, n);
16329                     }
16330                 }
16331             };
16332             this.expandPath(keys.join(this.pathSeparator), attr, f);
16333         }else{
16334             this.root.select();
16335             if(callback){
16336                 callback(true, this.root);
16337             }
16338         }
16339     },
16340
16341     getTreeEl : function(){
16342         return this.el;
16343     },
16344
16345     /**
16346      * Trigger rendering of this TreePanel
16347      */
16348     render : function(){
16349         if (this.innerCt) {
16350             return this; // stop it rendering more than once!!
16351         }
16352         
16353         this.innerCt = this.el.createChild({tag:"ul",
16354                cls:"x-tree-root-ct " +
16355                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16356
16357         if(this.containerScroll){
16358             Roo.dd.ScrollManager.register(this.el);
16359         }
16360         if((this.enableDD || this.enableDrop) && !this.dropZone){
16361            /**
16362             * The dropZone used by this tree if drop is enabled
16363             * @type Roo.tree.TreeDropZone
16364             */
16365              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16366                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16367            });
16368         }
16369         if((this.enableDD || this.enableDrag) && !this.dragZone){
16370            /**
16371             * The dragZone used by this tree if drag is enabled
16372             * @type Roo.tree.TreeDragZone
16373             */
16374             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16375                ddGroup: this.ddGroup || "TreeDD",
16376                scroll: this.ddScroll
16377            });
16378         }
16379         this.getSelectionModel().init(this);
16380         if (!this.root) {
16381             Roo.log("ROOT not set in tree");
16382             return this;
16383         }
16384         this.root.render();
16385         if(!this.rootVisible){
16386             this.root.renderChildren();
16387         }
16388         return this;
16389     }
16390 });/*
16391  * Based on:
16392  * Ext JS Library 1.1.1
16393  * Copyright(c) 2006-2007, Ext JS, LLC.
16394  *
16395  * Originally Released Under LGPL - original licence link has changed is not relivant.
16396  *
16397  * Fork - LGPL
16398  * <script type="text/javascript">
16399  */
16400  
16401
16402 /**
16403  * @class Roo.tree.DefaultSelectionModel
16404  * @extends Roo.util.Observable
16405  * The default single selection for a TreePanel.
16406  * @param {Object} cfg Configuration
16407  */
16408 Roo.tree.DefaultSelectionModel = function(cfg){
16409    this.selNode = null;
16410    
16411    
16412    
16413    this.addEvents({
16414        /**
16415         * @event selectionchange
16416         * Fires when the selected node changes
16417         * @param {DefaultSelectionModel} this
16418         * @param {TreeNode} node the new selection
16419         */
16420        "selectionchange" : true,
16421
16422        /**
16423         * @event beforeselect
16424         * Fires before the selected node changes, return false to cancel the change
16425         * @param {DefaultSelectionModel} this
16426         * @param {TreeNode} node the new selection
16427         * @param {TreeNode} node the old selection
16428         */
16429        "beforeselect" : true
16430    });
16431    
16432     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16433 };
16434
16435 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16436     init : function(tree){
16437         this.tree = tree;
16438         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16439         tree.on("click", this.onNodeClick, this);
16440     },
16441     
16442     onNodeClick : function(node, e){
16443         if (e.ctrlKey && this.selNode == node)  {
16444             this.unselect(node);
16445             return;
16446         }
16447         this.select(node);
16448     },
16449     
16450     /**
16451      * Select a node.
16452      * @param {TreeNode} node The node to select
16453      * @return {TreeNode} The selected node
16454      */
16455     select : function(node){
16456         var last = this.selNode;
16457         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16458             if(last){
16459                 last.ui.onSelectedChange(false);
16460             }
16461             this.selNode = node;
16462             node.ui.onSelectedChange(true);
16463             this.fireEvent("selectionchange", this, node, last);
16464         }
16465         return node;
16466     },
16467     
16468     /**
16469      * Deselect a node.
16470      * @param {TreeNode} node The node to unselect
16471      */
16472     unselect : function(node){
16473         if(this.selNode == node){
16474             this.clearSelections();
16475         }    
16476     },
16477     
16478     /**
16479      * Clear all selections
16480      */
16481     clearSelections : function(){
16482         var n = this.selNode;
16483         if(n){
16484             n.ui.onSelectedChange(false);
16485             this.selNode = null;
16486             this.fireEvent("selectionchange", this, null);
16487         }
16488         return n;
16489     },
16490     
16491     /**
16492      * Get the selected node
16493      * @return {TreeNode} The selected node
16494      */
16495     getSelectedNode : function(){
16496         return this.selNode;    
16497     },
16498     
16499     /**
16500      * Returns true if the node is selected
16501      * @param {TreeNode} node The node to check
16502      * @return {Boolean}
16503      */
16504     isSelected : function(node){
16505         return this.selNode == node;  
16506     },
16507
16508     /**
16509      * Selects the node above the selected node in the tree, intelligently walking the nodes
16510      * @return TreeNode The new selection
16511      */
16512     selectPrevious : function(){
16513         var s = this.selNode || this.lastSelNode;
16514         if(!s){
16515             return null;
16516         }
16517         var ps = s.previousSibling;
16518         if(ps){
16519             if(!ps.isExpanded() || ps.childNodes.length < 1){
16520                 return this.select(ps);
16521             } else{
16522                 var lc = ps.lastChild;
16523                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16524                     lc = lc.lastChild;
16525                 }
16526                 return this.select(lc);
16527             }
16528         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16529             return this.select(s.parentNode);
16530         }
16531         return null;
16532     },
16533
16534     /**
16535      * Selects the node above the selected node in the tree, intelligently walking the nodes
16536      * @return TreeNode The new selection
16537      */
16538     selectNext : function(){
16539         var s = this.selNode || this.lastSelNode;
16540         if(!s){
16541             return null;
16542         }
16543         if(s.firstChild && s.isExpanded()){
16544              return this.select(s.firstChild);
16545          }else if(s.nextSibling){
16546              return this.select(s.nextSibling);
16547          }else if(s.parentNode){
16548             var newS = null;
16549             s.parentNode.bubble(function(){
16550                 if(this.nextSibling){
16551                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16552                     return false;
16553                 }
16554             });
16555             return newS;
16556          }
16557         return null;
16558     },
16559
16560     onKeyDown : function(e){
16561         var s = this.selNode || this.lastSelNode;
16562         // undesirable, but required
16563         var sm = this;
16564         if(!s){
16565             return;
16566         }
16567         var k = e.getKey();
16568         switch(k){
16569              case e.DOWN:
16570                  e.stopEvent();
16571                  this.selectNext();
16572              break;
16573              case e.UP:
16574                  e.stopEvent();
16575                  this.selectPrevious();
16576              break;
16577              case e.RIGHT:
16578                  e.preventDefault();
16579                  if(s.hasChildNodes()){
16580                      if(!s.isExpanded()){
16581                          s.expand();
16582                      }else if(s.firstChild){
16583                          this.select(s.firstChild, e);
16584                      }
16585                  }
16586              break;
16587              case e.LEFT:
16588                  e.preventDefault();
16589                  if(s.hasChildNodes() && s.isExpanded()){
16590                      s.collapse();
16591                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16592                      this.select(s.parentNode, e);
16593                  }
16594              break;
16595         };
16596     }
16597 });
16598
16599 /**
16600  * @class Roo.tree.MultiSelectionModel
16601  * @extends Roo.util.Observable
16602  * Multi selection for a TreePanel.
16603  * @param {Object} cfg Configuration
16604  */
16605 Roo.tree.MultiSelectionModel = function(){
16606    this.selNodes = [];
16607    this.selMap = {};
16608    this.addEvents({
16609        /**
16610         * @event selectionchange
16611         * Fires when the selected nodes change
16612         * @param {MultiSelectionModel} this
16613         * @param {Array} nodes Array of the selected nodes
16614         */
16615        "selectionchange" : true
16616    });
16617    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16618    
16619 };
16620
16621 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16622     init : function(tree){
16623         this.tree = tree;
16624         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16625         tree.on("click", this.onNodeClick, this);
16626     },
16627     
16628     onNodeClick : function(node, e){
16629         this.select(node, e, e.ctrlKey);
16630     },
16631     
16632     /**
16633      * Select a node.
16634      * @param {TreeNode} node The node to select
16635      * @param {EventObject} e (optional) An event associated with the selection
16636      * @param {Boolean} keepExisting True to retain existing selections
16637      * @return {TreeNode} The selected node
16638      */
16639     select : function(node, e, keepExisting){
16640         if(keepExisting !== true){
16641             this.clearSelections(true);
16642         }
16643         if(this.isSelected(node)){
16644             this.lastSelNode = node;
16645             return node;
16646         }
16647         this.selNodes.push(node);
16648         this.selMap[node.id] = node;
16649         this.lastSelNode = node;
16650         node.ui.onSelectedChange(true);
16651         this.fireEvent("selectionchange", this, this.selNodes);
16652         return node;
16653     },
16654     
16655     /**
16656      * Deselect a node.
16657      * @param {TreeNode} node The node to unselect
16658      */
16659     unselect : function(node){
16660         if(this.selMap[node.id]){
16661             node.ui.onSelectedChange(false);
16662             var sn = this.selNodes;
16663             var index = -1;
16664             if(sn.indexOf){
16665                 index = sn.indexOf(node);
16666             }else{
16667                 for(var i = 0, len = sn.length; i < len; i++){
16668                     if(sn[i] == node){
16669                         index = i;
16670                         break;
16671                     }
16672                 }
16673             }
16674             if(index != -1){
16675                 this.selNodes.splice(index, 1);
16676             }
16677             delete this.selMap[node.id];
16678             this.fireEvent("selectionchange", this, this.selNodes);
16679         }
16680     },
16681     
16682     /**
16683      * Clear all selections
16684      */
16685     clearSelections : function(suppressEvent){
16686         var sn = this.selNodes;
16687         if(sn.length > 0){
16688             for(var i = 0, len = sn.length; i < len; i++){
16689                 sn[i].ui.onSelectedChange(false);
16690             }
16691             this.selNodes = [];
16692             this.selMap = {};
16693             if(suppressEvent !== true){
16694                 this.fireEvent("selectionchange", this, this.selNodes);
16695             }
16696         }
16697     },
16698     
16699     /**
16700      * Returns true if the node is selected
16701      * @param {TreeNode} node The node to check
16702      * @return {Boolean}
16703      */
16704     isSelected : function(node){
16705         return this.selMap[node.id] ? true : false;  
16706     },
16707     
16708     /**
16709      * Returns an array of the selected nodes
16710      * @return {Array}
16711      */
16712     getSelectedNodes : function(){
16713         return this.selNodes;    
16714     },
16715
16716     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16717
16718     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16719
16720     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16721 });/*
16722  * Based on:
16723  * Ext JS Library 1.1.1
16724  * Copyright(c) 2006-2007, Ext JS, LLC.
16725  *
16726  * Originally Released Under LGPL - original licence link has changed is not relivant.
16727  *
16728  * Fork - LGPL
16729  * <script type="text/javascript">
16730  */
16731  
16732 /**
16733  * @class Roo.tree.TreeNode
16734  * @extends Roo.data.Node
16735  * @cfg {String} text The text for this node
16736  * @cfg {Boolean} expanded true to start the node expanded
16737  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16738  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16739  * @cfg {Boolean} disabled true to start the node disabled
16740  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16741  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16742  * @cfg {String} cls A css class to be added to the node
16743  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16744  * @cfg {String} href URL of the link used for the node (defaults to #)
16745  * @cfg {String} hrefTarget target frame for the link
16746  * @cfg {String} qtip An Ext QuickTip for the node
16747  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16748  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16749  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16750  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16751  * (defaults to undefined with no checkbox rendered)
16752  * @constructor
16753  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16754  */
16755 Roo.tree.TreeNode = function(attributes){
16756     attributes = attributes || {};
16757     if(typeof attributes == "string"){
16758         attributes = {text: attributes};
16759     }
16760     this.childrenRendered = false;
16761     this.rendered = false;
16762     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16763     this.expanded = attributes.expanded === true;
16764     this.isTarget = attributes.isTarget !== false;
16765     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16766     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16767
16768     /**
16769      * Read-only. The text for this node. To change it use setText().
16770      * @type String
16771      */
16772     this.text = attributes.text;
16773     /**
16774      * True if this node is disabled.
16775      * @type Boolean
16776      */
16777     this.disabled = attributes.disabled === true;
16778
16779     this.addEvents({
16780         /**
16781         * @event textchange
16782         * Fires when the text for this node is changed
16783         * @param {Node} this This node
16784         * @param {String} text The new text
16785         * @param {String} oldText The old text
16786         */
16787         "textchange" : true,
16788         /**
16789         * @event beforeexpand
16790         * Fires before this node is expanded, return false to cancel.
16791         * @param {Node} this This node
16792         * @param {Boolean} deep
16793         * @param {Boolean} anim
16794         */
16795         "beforeexpand" : true,
16796         /**
16797         * @event beforecollapse
16798         * Fires before this node is collapsed, return false to cancel.
16799         * @param {Node} this This node
16800         * @param {Boolean} deep
16801         * @param {Boolean} anim
16802         */
16803         "beforecollapse" : true,
16804         /**
16805         * @event expand
16806         * Fires when this node is expanded
16807         * @param {Node} this This node
16808         */
16809         "expand" : true,
16810         /**
16811         * @event disabledchange
16812         * Fires when the disabled status of this node changes
16813         * @param {Node} this This node
16814         * @param {Boolean} disabled
16815         */
16816         "disabledchange" : true,
16817         /**
16818         * @event collapse
16819         * Fires when this node is collapsed
16820         * @param {Node} this This node
16821         */
16822         "collapse" : true,
16823         /**
16824         * @event beforeclick
16825         * Fires before click processing. Return false to cancel the default action.
16826         * @param {Node} this This node
16827         * @param {Roo.EventObject} e The event object
16828         */
16829         "beforeclick":true,
16830         /**
16831         * @event checkchange
16832         * Fires when a node with a checkbox's checked property changes
16833         * @param {Node} this This node
16834         * @param {Boolean} checked
16835         */
16836         "checkchange":true,
16837         /**
16838         * @event click
16839         * Fires when this node is clicked
16840         * @param {Node} this This node
16841         * @param {Roo.EventObject} e The event object
16842         */
16843         "click":true,
16844         /**
16845         * @event dblclick
16846         * Fires when this node is double clicked
16847         * @param {Node} this This node
16848         * @param {Roo.EventObject} e The event object
16849         */
16850         "dblclick":true,
16851         /**
16852         * @event contextmenu
16853         * Fires when this node is right clicked
16854         * @param {Node} this This node
16855         * @param {Roo.EventObject} e The event object
16856         */
16857         "contextmenu":true,
16858         /**
16859         * @event beforechildrenrendered
16860         * Fires right before the child nodes for this node are rendered
16861         * @param {Node} this This node
16862         */
16863         "beforechildrenrendered":true
16864     });
16865
16866     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16867
16868     /**
16869      * Read-only. The UI for this node
16870      * @type TreeNodeUI
16871      */
16872     this.ui = new uiClass(this);
16873     
16874     // finally support items[]
16875     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16876         return;
16877     }
16878     
16879     
16880     Roo.each(this.attributes.items, function(c) {
16881         this.appendChild(Roo.factory(c,Roo.Tree));
16882     }, this);
16883     delete this.attributes.items;
16884     
16885     
16886     
16887 };
16888 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16889     preventHScroll: true,
16890     /**
16891      * Returns true if this node is expanded
16892      * @return {Boolean}
16893      */
16894     isExpanded : function(){
16895         return this.expanded;
16896     },
16897
16898     /**
16899      * Returns the UI object for this node
16900      * @return {TreeNodeUI}
16901      */
16902     getUI : function(){
16903         return this.ui;
16904     },
16905
16906     // private override
16907     setFirstChild : function(node){
16908         var of = this.firstChild;
16909         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16910         if(this.childrenRendered && of && node != of){
16911             of.renderIndent(true, true);
16912         }
16913         if(this.rendered){
16914             this.renderIndent(true, true);
16915         }
16916     },
16917
16918     // private override
16919     setLastChild : function(node){
16920         var ol = this.lastChild;
16921         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16922         if(this.childrenRendered && ol && node != ol){
16923             ol.renderIndent(true, true);
16924         }
16925         if(this.rendered){
16926             this.renderIndent(true, true);
16927         }
16928     },
16929
16930     // these methods are overridden to provide lazy rendering support
16931     // private override
16932     appendChild : function()
16933     {
16934         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16935         if(node && this.childrenRendered){
16936             node.render();
16937         }
16938         this.ui.updateExpandIcon();
16939         return node;
16940     },
16941
16942     // private override
16943     removeChild : function(node){
16944         this.ownerTree.getSelectionModel().unselect(node);
16945         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16946         // if it's been rendered remove dom node
16947         if(this.childrenRendered){
16948             node.ui.remove();
16949         }
16950         if(this.childNodes.length < 1){
16951             this.collapse(false, false);
16952         }else{
16953             this.ui.updateExpandIcon();
16954         }
16955         if(!this.firstChild) {
16956             this.childrenRendered = false;
16957         }
16958         return node;
16959     },
16960
16961     // private override
16962     insertBefore : function(node, refNode){
16963         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16964         if(newNode && refNode && this.childrenRendered){
16965             node.render();
16966         }
16967         this.ui.updateExpandIcon();
16968         return newNode;
16969     },
16970
16971     /**
16972      * Sets the text for this node
16973      * @param {String} text
16974      */
16975     setText : function(text){
16976         var oldText = this.text;
16977         this.text = text;
16978         this.attributes.text = text;
16979         if(this.rendered){ // event without subscribing
16980             this.ui.onTextChange(this, text, oldText);
16981         }
16982         this.fireEvent("textchange", this, text, oldText);
16983     },
16984
16985     /**
16986      * Triggers selection of this node
16987      */
16988     select : function(){
16989         this.getOwnerTree().getSelectionModel().select(this);
16990     },
16991
16992     /**
16993      * Triggers deselection of this node
16994      */
16995     unselect : function(){
16996         this.getOwnerTree().getSelectionModel().unselect(this);
16997     },
16998
16999     /**
17000      * Returns true if this node is selected
17001      * @return {Boolean}
17002      */
17003     isSelected : function(){
17004         return this.getOwnerTree().getSelectionModel().isSelected(this);
17005     },
17006
17007     /**
17008      * Expand this node.
17009      * @param {Boolean} deep (optional) True to expand all children as well
17010      * @param {Boolean} anim (optional) false to cancel the default animation
17011      * @param {Function} callback (optional) A callback to be called when
17012      * expanding this node completes (does not wait for deep expand to complete).
17013      * Called with 1 parameter, this node.
17014      */
17015     expand : function(deep, anim, callback){
17016         if(!this.expanded){
17017             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17018                 return;
17019             }
17020             if(!this.childrenRendered){
17021                 this.renderChildren();
17022             }
17023             this.expanded = true;
17024             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17025                 this.ui.animExpand(function(){
17026                     this.fireEvent("expand", this);
17027                     if(typeof callback == "function"){
17028                         callback(this);
17029                     }
17030                     if(deep === true){
17031                         this.expandChildNodes(true);
17032                     }
17033                 }.createDelegate(this));
17034                 return;
17035             }else{
17036                 this.ui.expand();
17037                 this.fireEvent("expand", this);
17038                 if(typeof callback == "function"){
17039                     callback(this);
17040                 }
17041             }
17042         }else{
17043            if(typeof callback == "function"){
17044                callback(this);
17045            }
17046         }
17047         if(deep === true){
17048             this.expandChildNodes(true);
17049         }
17050     },
17051
17052     isHiddenRoot : function(){
17053         return this.isRoot && !this.getOwnerTree().rootVisible;
17054     },
17055
17056     /**
17057      * Collapse this node.
17058      * @param {Boolean} deep (optional) True to collapse all children as well
17059      * @param {Boolean} anim (optional) false to cancel the default animation
17060      */
17061     collapse : function(deep, anim){
17062         if(this.expanded && !this.isHiddenRoot()){
17063             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17064                 return;
17065             }
17066             this.expanded = false;
17067             if((this.getOwnerTree().animate && anim !== false) || anim){
17068                 this.ui.animCollapse(function(){
17069                     this.fireEvent("collapse", this);
17070                     if(deep === true){
17071                         this.collapseChildNodes(true);
17072                     }
17073                 }.createDelegate(this));
17074                 return;
17075             }else{
17076                 this.ui.collapse();
17077                 this.fireEvent("collapse", this);
17078             }
17079         }
17080         if(deep === true){
17081             var cs = this.childNodes;
17082             for(var i = 0, len = cs.length; i < len; i++) {
17083                 cs[i].collapse(true, false);
17084             }
17085         }
17086     },
17087
17088     // private
17089     delayedExpand : function(delay){
17090         if(!this.expandProcId){
17091             this.expandProcId = this.expand.defer(delay, this);
17092         }
17093     },
17094
17095     // private
17096     cancelExpand : function(){
17097         if(this.expandProcId){
17098             clearTimeout(this.expandProcId);
17099         }
17100         this.expandProcId = false;
17101     },
17102
17103     /**
17104      * Toggles expanded/collapsed state of the node
17105      */
17106     toggle : function(){
17107         if(this.expanded){
17108             this.collapse();
17109         }else{
17110             this.expand();
17111         }
17112     },
17113
17114     /**
17115      * Ensures all parent nodes are expanded
17116      */
17117     ensureVisible : function(callback){
17118         var tree = this.getOwnerTree();
17119         tree.expandPath(this.parentNode.getPath(), false, function(){
17120             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17121             Roo.callback(callback);
17122         }.createDelegate(this));
17123     },
17124
17125     /**
17126      * Expand all child nodes
17127      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17128      */
17129     expandChildNodes : function(deep){
17130         var cs = this.childNodes;
17131         for(var i = 0, len = cs.length; i < len; i++) {
17132                 cs[i].expand(deep);
17133         }
17134     },
17135
17136     /**
17137      * Collapse all child nodes
17138      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17139      */
17140     collapseChildNodes : function(deep){
17141         var cs = this.childNodes;
17142         for(var i = 0, len = cs.length; i < len; i++) {
17143                 cs[i].collapse(deep);
17144         }
17145     },
17146
17147     /**
17148      * Disables this node
17149      */
17150     disable : function(){
17151         this.disabled = true;
17152         this.unselect();
17153         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17154             this.ui.onDisableChange(this, true);
17155         }
17156         this.fireEvent("disabledchange", this, true);
17157     },
17158
17159     /**
17160      * Enables this node
17161      */
17162     enable : function(){
17163         this.disabled = false;
17164         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17165             this.ui.onDisableChange(this, false);
17166         }
17167         this.fireEvent("disabledchange", this, false);
17168     },
17169
17170     // private
17171     renderChildren : function(suppressEvent){
17172         if(suppressEvent !== false){
17173             this.fireEvent("beforechildrenrendered", this);
17174         }
17175         var cs = this.childNodes;
17176         for(var i = 0, len = cs.length; i < len; i++){
17177             cs[i].render(true);
17178         }
17179         this.childrenRendered = true;
17180     },
17181
17182     // private
17183     sort : function(fn, scope){
17184         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17185         if(this.childrenRendered){
17186             var cs = this.childNodes;
17187             for(var i = 0, len = cs.length; i < len; i++){
17188                 cs[i].render(true);
17189             }
17190         }
17191     },
17192
17193     // private
17194     render : function(bulkRender){
17195         this.ui.render(bulkRender);
17196         if(!this.rendered){
17197             this.rendered = true;
17198             if(this.expanded){
17199                 this.expanded = false;
17200                 this.expand(false, false);
17201             }
17202         }
17203     },
17204
17205     // private
17206     renderIndent : function(deep, refresh){
17207         if(refresh){
17208             this.ui.childIndent = null;
17209         }
17210         this.ui.renderIndent();
17211         if(deep === true && this.childrenRendered){
17212             var cs = this.childNodes;
17213             for(var i = 0, len = cs.length; i < len; i++){
17214                 cs[i].renderIndent(true, refresh);
17215             }
17216         }
17217     }
17218 });/*
17219  * Based on:
17220  * Ext JS Library 1.1.1
17221  * Copyright(c) 2006-2007, Ext JS, LLC.
17222  *
17223  * Originally Released Under LGPL - original licence link has changed is not relivant.
17224  *
17225  * Fork - LGPL
17226  * <script type="text/javascript">
17227  */
17228  
17229 /**
17230  * @class Roo.tree.AsyncTreeNode
17231  * @extends Roo.tree.TreeNode
17232  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17233  * @constructor
17234  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17235  */
17236  Roo.tree.AsyncTreeNode = function(config){
17237     this.loaded = false;
17238     this.loading = false;
17239     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17240     /**
17241     * @event beforeload
17242     * Fires before this node is loaded, return false to cancel
17243     * @param {Node} this This node
17244     */
17245     this.addEvents({'beforeload':true, 'load': true});
17246     /**
17247     * @event load
17248     * Fires when this node is loaded
17249     * @param {Node} this This node
17250     */
17251     /**
17252      * The loader used by this node (defaults to using the tree's defined loader)
17253      * @type TreeLoader
17254      * @property loader
17255      */
17256 };
17257 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17258     expand : function(deep, anim, callback){
17259         if(this.loading){ // if an async load is already running, waiting til it's done
17260             var timer;
17261             var f = function(){
17262                 if(!this.loading){ // done loading
17263                     clearInterval(timer);
17264                     this.expand(deep, anim, callback);
17265                 }
17266             }.createDelegate(this);
17267             timer = setInterval(f, 200);
17268             return;
17269         }
17270         if(!this.loaded){
17271             if(this.fireEvent("beforeload", this) === false){
17272                 return;
17273             }
17274             this.loading = true;
17275             this.ui.beforeLoad(this);
17276             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17277             if(loader){
17278                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17279                 return;
17280             }
17281         }
17282         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17283     },
17284     
17285     /**
17286      * Returns true if this node is currently loading
17287      * @return {Boolean}
17288      */
17289     isLoading : function(){
17290         return this.loading;  
17291     },
17292     
17293     loadComplete : function(deep, anim, callback){
17294         this.loading = false;
17295         this.loaded = true;
17296         this.ui.afterLoad(this);
17297         this.fireEvent("load", this);
17298         this.expand(deep, anim, callback);
17299     },
17300     
17301     /**
17302      * Returns true if this node has been loaded
17303      * @return {Boolean}
17304      */
17305     isLoaded : function(){
17306         return this.loaded;
17307     },
17308     
17309     hasChildNodes : function(){
17310         if(!this.isLeaf() && !this.loaded){
17311             return true;
17312         }else{
17313             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17314         }
17315     },
17316
17317     /**
17318      * Trigger a reload for this node
17319      * @param {Function} callback
17320      */
17321     reload : function(callback){
17322         this.collapse(false, false);
17323         while(this.firstChild){
17324             this.removeChild(this.firstChild);
17325         }
17326         this.childrenRendered = false;
17327         this.loaded = false;
17328         if(this.isHiddenRoot()){
17329             this.expanded = false;
17330         }
17331         this.expand(false, false, callback);
17332     }
17333 });/*
17334  * Based on:
17335  * Ext JS Library 1.1.1
17336  * Copyright(c) 2006-2007, Ext JS, LLC.
17337  *
17338  * Originally Released Under LGPL - original licence link has changed is not relivant.
17339  *
17340  * Fork - LGPL
17341  * <script type="text/javascript">
17342  */
17343  
17344 /**
17345  * @class Roo.tree.TreeNodeUI
17346  * @constructor
17347  * @param {Object} node The node to render
17348  * The TreeNode UI implementation is separate from the
17349  * tree implementation. Unless you are customizing the tree UI,
17350  * you should never have to use this directly.
17351  */
17352 Roo.tree.TreeNodeUI = function(node){
17353     this.node = node;
17354     this.rendered = false;
17355     this.animating = false;
17356     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17357 };
17358
17359 Roo.tree.TreeNodeUI.prototype = {
17360     removeChild : function(node){
17361         if(this.rendered){
17362             this.ctNode.removeChild(node.ui.getEl());
17363         }
17364     },
17365
17366     beforeLoad : function(){
17367          this.addClass("x-tree-node-loading");
17368     },
17369
17370     afterLoad : function(){
17371          this.removeClass("x-tree-node-loading");
17372     },
17373
17374     onTextChange : function(node, text, oldText){
17375         if(this.rendered){
17376             this.textNode.innerHTML = text;
17377         }
17378     },
17379
17380     onDisableChange : function(node, state){
17381         this.disabled = state;
17382         if(state){
17383             this.addClass("x-tree-node-disabled");
17384         }else{
17385             this.removeClass("x-tree-node-disabled");
17386         }
17387     },
17388
17389     onSelectedChange : function(state){
17390         if(state){
17391             this.focus();
17392             this.addClass("x-tree-selected");
17393         }else{
17394             //this.blur();
17395             this.removeClass("x-tree-selected");
17396         }
17397     },
17398
17399     onMove : function(tree, node, oldParent, newParent, index, refNode){
17400         this.childIndent = null;
17401         if(this.rendered){
17402             var targetNode = newParent.ui.getContainer();
17403             if(!targetNode){//target not rendered
17404                 this.holder = document.createElement("div");
17405                 this.holder.appendChild(this.wrap);
17406                 return;
17407             }
17408             var insertBefore = refNode ? refNode.ui.getEl() : null;
17409             if(insertBefore){
17410                 targetNode.insertBefore(this.wrap, insertBefore);
17411             }else{
17412                 targetNode.appendChild(this.wrap);
17413             }
17414             this.node.renderIndent(true);
17415         }
17416     },
17417
17418     addClass : function(cls){
17419         if(this.elNode){
17420             Roo.fly(this.elNode).addClass(cls);
17421         }
17422     },
17423
17424     removeClass : function(cls){
17425         if(this.elNode){
17426             Roo.fly(this.elNode).removeClass(cls);
17427         }
17428     },
17429
17430     remove : function(){
17431         if(this.rendered){
17432             this.holder = document.createElement("div");
17433             this.holder.appendChild(this.wrap);
17434         }
17435     },
17436
17437     fireEvent : function(){
17438         return this.node.fireEvent.apply(this.node, arguments);
17439     },
17440
17441     initEvents : function(){
17442         this.node.on("move", this.onMove, this);
17443         var E = Roo.EventManager;
17444         var a = this.anchor;
17445
17446         var el = Roo.fly(a, '_treeui');
17447
17448         if(Roo.isOpera){ // opera render bug ignores the CSS
17449             el.setStyle("text-decoration", "none");
17450         }
17451
17452         el.on("click", this.onClick, this);
17453         el.on("dblclick", this.onDblClick, this);
17454
17455         if(this.checkbox){
17456             Roo.EventManager.on(this.checkbox,
17457                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17458         }
17459
17460         el.on("contextmenu", this.onContextMenu, this);
17461
17462         var icon = Roo.fly(this.iconNode);
17463         icon.on("click", this.onClick, this);
17464         icon.on("dblclick", this.onDblClick, this);
17465         icon.on("contextmenu", this.onContextMenu, this);
17466         E.on(this.ecNode, "click", this.ecClick, this, true);
17467
17468         if(this.node.disabled){
17469             this.addClass("x-tree-node-disabled");
17470         }
17471         if(this.node.hidden){
17472             this.addClass("x-tree-node-disabled");
17473         }
17474         var ot = this.node.getOwnerTree();
17475         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17476         if(dd && (!this.node.isRoot || ot.rootVisible)){
17477             Roo.dd.Registry.register(this.elNode, {
17478                 node: this.node,
17479                 handles: this.getDDHandles(),
17480                 isHandle: false
17481             });
17482         }
17483     },
17484
17485     getDDHandles : function(){
17486         return [this.iconNode, this.textNode];
17487     },
17488
17489     hide : function(){
17490         if(this.rendered){
17491             this.wrap.style.display = "none";
17492         }
17493     },
17494
17495     show : function(){
17496         if(this.rendered){
17497             this.wrap.style.display = "";
17498         }
17499     },
17500
17501     onContextMenu : function(e){
17502         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17503             e.preventDefault();
17504             this.focus();
17505             this.fireEvent("contextmenu", this.node, e);
17506         }
17507     },
17508
17509     onClick : function(e){
17510         if(this.dropping){
17511             e.stopEvent();
17512             return;
17513         }
17514         if(this.fireEvent("beforeclick", this.node, e) !== false){
17515             if(!this.disabled && this.node.attributes.href){
17516                 this.fireEvent("click", this.node, e);
17517                 return;
17518             }
17519             e.preventDefault();
17520             if(this.disabled){
17521                 return;
17522             }
17523
17524             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17525                 this.node.toggle();
17526             }
17527
17528             this.fireEvent("click", this.node, e);
17529         }else{
17530             e.stopEvent();
17531         }
17532     },
17533
17534     onDblClick : function(e){
17535         e.preventDefault();
17536         if(this.disabled){
17537             return;
17538         }
17539         if(this.checkbox){
17540             this.toggleCheck();
17541         }
17542         if(!this.animating && this.node.hasChildNodes()){
17543             this.node.toggle();
17544         }
17545         this.fireEvent("dblclick", this.node, e);
17546     },
17547
17548     onCheckChange : function(){
17549         var checked = this.checkbox.checked;
17550         this.node.attributes.checked = checked;
17551         this.fireEvent('checkchange', this.node, checked);
17552     },
17553
17554     ecClick : function(e){
17555         if(!this.animating && this.node.hasChildNodes()){
17556             this.node.toggle();
17557         }
17558     },
17559
17560     startDrop : function(){
17561         this.dropping = true;
17562     },
17563
17564     // delayed drop so the click event doesn't get fired on a drop
17565     endDrop : function(){
17566        setTimeout(function(){
17567            this.dropping = false;
17568        }.createDelegate(this), 50);
17569     },
17570
17571     expand : function(){
17572         this.updateExpandIcon();
17573         this.ctNode.style.display = "";
17574     },
17575
17576     focus : function(){
17577         if(!this.node.preventHScroll){
17578             try{this.anchor.focus();
17579             }catch(e){}
17580         }else if(!Roo.isIE){
17581             try{
17582                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17583                 var l = noscroll.scrollLeft;
17584                 this.anchor.focus();
17585                 noscroll.scrollLeft = l;
17586             }catch(e){}
17587         }
17588     },
17589
17590     toggleCheck : function(value){
17591         var cb = this.checkbox;
17592         if(cb){
17593             cb.checked = (value === undefined ? !cb.checked : value);
17594         }
17595     },
17596
17597     blur : function(){
17598         try{
17599             this.anchor.blur();
17600         }catch(e){}
17601     },
17602
17603     animExpand : function(callback){
17604         var ct = Roo.get(this.ctNode);
17605         ct.stopFx();
17606         if(!this.node.hasChildNodes()){
17607             this.updateExpandIcon();
17608             this.ctNode.style.display = "";
17609             Roo.callback(callback);
17610             return;
17611         }
17612         this.animating = true;
17613         this.updateExpandIcon();
17614
17615         ct.slideIn('t', {
17616            callback : function(){
17617                this.animating = false;
17618                Roo.callback(callback);
17619             },
17620             scope: this,
17621             duration: this.node.ownerTree.duration || .25
17622         });
17623     },
17624
17625     highlight : function(){
17626         var tree = this.node.getOwnerTree();
17627         Roo.fly(this.wrap).highlight(
17628             tree.hlColor || "C3DAF9",
17629             {endColor: tree.hlBaseColor}
17630         );
17631     },
17632
17633     collapse : function(){
17634         this.updateExpandIcon();
17635         this.ctNode.style.display = "none";
17636     },
17637
17638     animCollapse : function(callback){
17639         var ct = Roo.get(this.ctNode);
17640         ct.enableDisplayMode('block');
17641         ct.stopFx();
17642
17643         this.animating = true;
17644         this.updateExpandIcon();
17645
17646         ct.slideOut('t', {
17647             callback : function(){
17648                this.animating = false;
17649                Roo.callback(callback);
17650             },
17651             scope: this,
17652             duration: this.node.ownerTree.duration || .25
17653         });
17654     },
17655
17656     getContainer : function(){
17657         return this.ctNode;
17658     },
17659
17660     getEl : function(){
17661         return this.wrap;
17662     },
17663
17664     appendDDGhost : function(ghostNode){
17665         ghostNode.appendChild(this.elNode.cloneNode(true));
17666     },
17667
17668     getDDRepairXY : function(){
17669         return Roo.lib.Dom.getXY(this.iconNode);
17670     },
17671
17672     onRender : function(){
17673         this.render();
17674     },
17675
17676     render : function(bulkRender){
17677         var n = this.node, a = n.attributes;
17678         var targetNode = n.parentNode ?
17679               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17680
17681         if(!this.rendered){
17682             this.rendered = true;
17683
17684             this.renderElements(n, a, targetNode, bulkRender);
17685
17686             if(a.qtip){
17687                if(this.textNode.setAttributeNS){
17688                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17689                    if(a.qtipTitle){
17690                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17691                    }
17692                }else{
17693                    this.textNode.setAttribute("ext:qtip", a.qtip);
17694                    if(a.qtipTitle){
17695                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17696                    }
17697                }
17698             }else if(a.qtipCfg){
17699                 a.qtipCfg.target = Roo.id(this.textNode);
17700                 Roo.QuickTips.register(a.qtipCfg);
17701             }
17702             this.initEvents();
17703             if(!this.node.expanded){
17704                 this.updateExpandIcon();
17705             }
17706         }else{
17707             if(bulkRender === true) {
17708                 targetNode.appendChild(this.wrap);
17709             }
17710         }
17711     },
17712
17713     renderElements : function(n, a, targetNode, bulkRender)
17714     {
17715         // add some indent caching, this helps performance when rendering a large tree
17716         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17717         var t = n.getOwnerTree();
17718         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17719         if (typeof(n.attributes.html) != 'undefined') {
17720             txt = n.attributes.html;
17721         }
17722         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17723         var cb = typeof a.checked == 'boolean';
17724         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17725         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17726             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17727             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17728             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17729             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17730             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17731              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17732                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17733             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17734             "</li>"];
17735
17736         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17737             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17738                                 n.nextSibling.ui.getEl(), buf.join(""));
17739         }else{
17740             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17741         }
17742
17743         this.elNode = this.wrap.childNodes[0];
17744         this.ctNode = this.wrap.childNodes[1];
17745         var cs = this.elNode.childNodes;
17746         this.indentNode = cs[0];
17747         this.ecNode = cs[1];
17748         this.iconNode = cs[2];
17749         var index = 3;
17750         if(cb){
17751             this.checkbox = cs[3];
17752             index++;
17753         }
17754         this.anchor = cs[index];
17755         this.textNode = cs[index].firstChild;
17756     },
17757
17758     getAnchor : function(){
17759         return this.anchor;
17760     },
17761
17762     getTextEl : function(){
17763         return this.textNode;
17764     },
17765
17766     getIconEl : function(){
17767         return this.iconNode;
17768     },
17769
17770     isChecked : function(){
17771         return this.checkbox ? this.checkbox.checked : false;
17772     },
17773
17774     updateExpandIcon : function(){
17775         if(this.rendered){
17776             var n = this.node, c1, c2;
17777             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17778             var hasChild = n.hasChildNodes();
17779             if(hasChild){
17780                 if(n.expanded){
17781                     cls += "-minus";
17782                     c1 = "x-tree-node-collapsed";
17783                     c2 = "x-tree-node-expanded";
17784                 }else{
17785                     cls += "-plus";
17786                     c1 = "x-tree-node-expanded";
17787                     c2 = "x-tree-node-collapsed";
17788                 }
17789                 if(this.wasLeaf){
17790                     this.removeClass("x-tree-node-leaf");
17791                     this.wasLeaf = false;
17792                 }
17793                 if(this.c1 != c1 || this.c2 != c2){
17794                     Roo.fly(this.elNode).replaceClass(c1, c2);
17795                     this.c1 = c1; this.c2 = c2;
17796                 }
17797             }else{
17798                 // this changes non-leafs into leafs if they have no children.
17799                 // it's not very rational behaviour..
17800                 
17801                 if(!this.wasLeaf && this.node.leaf){
17802                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17803                     delete this.c1;
17804                     delete this.c2;
17805                     this.wasLeaf = true;
17806                 }
17807             }
17808             var ecc = "x-tree-ec-icon "+cls;
17809             if(this.ecc != ecc){
17810                 this.ecNode.className = ecc;
17811                 this.ecc = ecc;
17812             }
17813         }
17814     },
17815
17816     getChildIndent : function(){
17817         if(!this.childIndent){
17818             var buf = [];
17819             var p = this.node;
17820             while(p){
17821                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17822                     if(!p.isLast()) {
17823                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17824                     } else {
17825                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17826                     }
17827                 }
17828                 p = p.parentNode;
17829             }
17830             this.childIndent = buf.join("");
17831         }
17832         return this.childIndent;
17833     },
17834
17835     renderIndent : function(){
17836         if(this.rendered){
17837             var indent = "";
17838             var p = this.node.parentNode;
17839             if(p){
17840                 indent = p.ui.getChildIndent();
17841             }
17842             if(this.indentMarkup != indent){ // don't rerender if not required
17843                 this.indentNode.innerHTML = indent;
17844                 this.indentMarkup = indent;
17845             }
17846             this.updateExpandIcon();
17847         }
17848     }
17849 };
17850
17851 Roo.tree.RootTreeNodeUI = function(){
17852     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17853 };
17854 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17855     render : function(){
17856         if(!this.rendered){
17857             var targetNode = this.node.ownerTree.innerCt.dom;
17858             this.node.expanded = true;
17859             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17860             this.wrap = this.ctNode = targetNode.firstChild;
17861         }
17862     },
17863     collapse : function(){
17864     },
17865     expand : function(){
17866     }
17867 });/*
17868  * Based on:
17869  * Ext JS Library 1.1.1
17870  * Copyright(c) 2006-2007, Ext JS, LLC.
17871  *
17872  * Originally Released Under LGPL - original licence link has changed is not relivant.
17873  *
17874  * Fork - LGPL
17875  * <script type="text/javascript">
17876  */
17877 /**
17878  * @class Roo.tree.TreeLoader
17879  * @extends Roo.util.Observable
17880  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17881  * nodes from a specified URL. The response must be a javascript Array definition
17882  * who's elements are node definition objects. eg:
17883  * <pre><code>
17884 {  success : true,
17885    data :      [
17886    
17887     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17888     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17889     ]
17890 }
17891
17892
17893 </code></pre>
17894  * <br><br>
17895  * The old style respose with just an array is still supported, but not recommended.
17896  * <br><br>
17897  *
17898  * A server request is sent, and child nodes are loaded only when a node is expanded.
17899  * The loading node's id is passed to the server under the parameter name "node" to
17900  * enable the server to produce the correct child nodes.
17901  * <br><br>
17902  * To pass extra parameters, an event handler may be attached to the "beforeload"
17903  * event, and the parameters specified in the TreeLoader's baseParams property:
17904  * <pre><code>
17905     myTreeLoader.on("beforeload", function(treeLoader, node) {
17906         this.baseParams.category = node.attributes.category;
17907     }, this);
17908 </code></pre><
17909  * This would pass an HTTP parameter called "category" to the server containing
17910  * the value of the Node's "category" attribute.
17911  * @constructor
17912  * Creates a new Treeloader.
17913  * @param {Object} config A config object containing config properties.
17914  */
17915 Roo.tree.TreeLoader = function(config){
17916     this.baseParams = {};
17917     this.requestMethod = "POST";
17918     Roo.apply(this, config);
17919
17920     this.addEvents({
17921     
17922         /**
17923          * @event beforeload
17924          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17925          * @param {Object} This TreeLoader object.
17926          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17927          * @param {Object} callback The callback function specified in the {@link #load} call.
17928          */
17929         beforeload : true,
17930         /**
17931          * @event load
17932          * Fires when the node has been successfuly loaded.
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         load : true,
17938         /**
17939          * @event loadexception
17940          * Fires if the network request failed.
17941          * @param {Object} This TreeLoader object.
17942          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17943          * @param {Object} response The response object containing the data from the server.
17944          */
17945         loadexception : true,
17946         /**
17947          * @event create
17948          * Fires before a node is created, enabling you to return custom Node types 
17949          * @param {Object} This TreeLoader object.
17950          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17951          */
17952         create : true
17953     });
17954
17955     Roo.tree.TreeLoader.superclass.constructor.call(this);
17956 };
17957
17958 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17959     /**
17960     * @cfg {String} dataUrl The URL from which to request a Json string which
17961     * specifies an array of node definition object representing the child nodes
17962     * to be loaded.
17963     */
17964     /**
17965     * @cfg {String} requestMethod either GET or POST
17966     * defaults to POST (due to BC)
17967     * to be loaded.
17968     */
17969     /**
17970     * @cfg {Object} baseParams (optional) An object containing properties which
17971     * specify HTTP parameters to be passed to each request for child nodes.
17972     */
17973     /**
17974     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17975     * created by this loader. If the attributes sent by the server have an attribute in this object,
17976     * they take priority.
17977     */
17978     /**
17979     * @cfg {Object} uiProviders (optional) An object containing properties which
17980     * 
17981     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17982     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17983     * <i>uiProvider</i> attribute of a returned child node is a string rather
17984     * than a reference to a TreeNodeUI implementation, this that string value
17985     * is used as a property name in the uiProviders object. You can define the provider named
17986     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17987     */
17988     uiProviders : {},
17989
17990     /**
17991     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17992     * child nodes before loading.
17993     */
17994     clearOnLoad : true,
17995
17996     /**
17997     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17998     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17999     * Grid query { data : [ .....] }
18000     */
18001     
18002     root : false,
18003      /**
18004     * @cfg {String} queryParam (optional) 
18005     * Name of the query as it will be passed on the querystring (defaults to 'node')
18006     * eg. the request will be ?node=[id]
18007     */
18008     
18009     
18010     queryParam: false,
18011     
18012     /**
18013      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18014      * This is called automatically when a node is expanded, but may be used to reload
18015      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18016      * @param {Roo.tree.TreeNode} node
18017      * @param {Function} callback
18018      */
18019     load : function(node, callback){
18020         if(this.clearOnLoad){
18021             while(node.firstChild){
18022                 node.removeChild(node.firstChild);
18023             }
18024         }
18025         if(node.attributes.children){ // preloaded json children
18026             var cs = node.attributes.children;
18027             for(var i = 0, len = cs.length; i < len; i++){
18028                 node.appendChild(this.createNode(cs[i]));
18029             }
18030             if(typeof callback == "function"){
18031                 callback();
18032             }
18033         }else if(this.dataUrl){
18034             this.requestData(node, callback);
18035         }
18036     },
18037
18038     getParams: function(node){
18039         var buf = [], bp = this.baseParams;
18040         for(var key in bp){
18041             if(typeof bp[key] != "function"){
18042                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18043             }
18044         }
18045         var n = this.queryParam === false ? 'node' : this.queryParam;
18046         buf.push(n + "=", encodeURIComponent(node.id));
18047         return buf.join("");
18048     },
18049
18050     requestData : function(node, callback){
18051         if(this.fireEvent("beforeload", this, node, callback) !== false){
18052             this.transId = Roo.Ajax.request({
18053                 method:this.requestMethod,
18054                 url: this.dataUrl||this.url,
18055                 success: this.handleResponse,
18056                 failure: this.handleFailure,
18057                 scope: this,
18058                 argument: {callback: callback, node: node},
18059                 params: this.getParams(node)
18060             });
18061         }else{
18062             // if the load is cancelled, make sure we notify
18063             // the node that we are done
18064             if(typeof callback == "function"){
18065                 callback();
18066             }
18067         }
18068     },
18069
18070     isLoading : function(){
18071         return this.transId ? true : false;
18072     },
18073
18074     abort : function(){
18075         if(this.isLoading()){
18076             Roo.Ajax.abort(this.transId);
18077         }
18078     },
18079
18080     // private
18081     createNode : function(attr)
18082     {
18083         // apply baseAttrs, nice idea Corey!
18084         if(this.baseAttrs){
18085             Roo.applyIf(attr, this.baseAttrs);
18086         }
18087         if(this.applyLoader !== false){
18088             attr.loader = this;
18089         }
18090         // uiProvider = depreciated..
18091         
18092         if(typeof(attr.uiProvider) == 'string'){
18093            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18094                 /**  eval:var:attr */ eval(attr.uiProvider);
18095         }
18096         if(typeof(this.uiProviders['default']) != 'undefined') {
18097             attr.uiProvider = this.uiProviders['default'];
18098         }
18099         
18100         this.fireEvent('create', this, attr);
18101         
18102         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18103         return(attr.leaf ?
18104                         new Roo.tree.TreeNode(attr) :
18105                         new Roo.tree.AsyncTreeNode(attr));
18106     },
18107
18108     processResponse : function(response, node, callback)
18109     {
18110         var json = response.responseText;
18111         try {
18112             
18113             var o = Roo.decode(json);
18114             
18115             if (this.root === false && typeof(o.success) != undefined) {
18116                 this.root = 'data'; // the default behaviour for list like data..
18117                 }
18118                 
18119             if (this.root !== false &&  !o.success) {
18120                 // it's a failure condition.
18121                 var a = response.argument;
18122                 this.fireEvent("loadexception", this, a.node, response);
18123                 Roo.log("Load failed - should have a handler really");
18124                 return;
18125             }
18126             
18127             
18128             
18129             if (this.root !== false) {
18130                  o = o[this.root];
18131             }
18132             
18133             for(var i = 0, len = o.length; i < len; i++){
18134                 var n = this.createNode(o[i]);
18135                 if(n){
18136                     node.appendChild(n);
18137                 }
18138             }
18139             if(typeof callback == "function"){
18140                 callback(this, node);
18141             }
18142         }catch(e){
18143             this.handleFailure(response);
18144         }
18145     },
18146
18147     handleResponse : function(response){
18148         this.transId = false;
18149         var a = response.argument;
18150         this.processResponse(response, a.node, a.callback);
18151         this.fireEvent("load", this, a.node, response);
18152     },
18153
18154     handleFailure : function(response)
18155     {
18156         // should handle failure better..
18157         this.transId = false;
18158         var a = response.argument;
18159         this.fireEvent("loadexception", this, a.node, response);
18160         if(typeof a.callback == "function"){
18161             a.callback(this, a.node);
18162         }
18163     }
18164 });/*
18165  * Based on:
18166  * Ext JS Library 1.1.1
18167  * Copyright(c) 2006-2007, Ext JS, LLC.
18168  *
18169  * Originally Released Under LGPL - original licence link has changed is not relivant.
18170  *
18171  * Fork - LGPL
18172  * <script type="text/javascript">
18173  */
18174
18175 /**
18176 * @class Roo.tree.TreeFilter
18177 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18178 * @param {TreePanel} tree
18179 * @param {Object} config (optional)
18180  */
18181 Roo.tree.TreeFilter = function(tree, config){
18182     this.tree = tree;
18183     this.filtered = {};
18184     Roo.apply(this, config);
18185 };
18186
18187 Roo.tree.TreeFilter.prototype = {
18188     clearBlank:false,
18189     reverse:false,
18190     autoClear:false,
18191     remove:false,
18192
18193      /**
18194      * Filter the data by a specific attribute.
18195      * @param {String/RegExp} value Either string that the attribute value
18196      * should start with or a RegExp to test against the attribute
18197      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18198      * @param {TreeNode} startNode (optional) The node to start the filter at.
18199      */
18200     filter : function(value, attr, startNode){
18201         attr = attr || "text";
18202         var f;
18203         if(typeof value == "string"){
18204             var vlen = value.length;
18205             // auto clear empty filter
18206             if(vlen == 0 && this.clearBlank){
18207                 this.clear();
18208                 return;
18209             }
18210             value = value.toLowerCase();
18211             f = function(n){
18212                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18213             };
18214         }else if(value.exec){ // regex?
18215             f = function(n){
18216                 return value.test(n.attributes[attr]);
18217             };
18218         }else{
18219             throw 'Illegal filter type, must be string or regex';
18220         }
18221         this.filterBy(f, null, startNode);
18222         },
18223
18224     /**
18225      * Filter by a function. The passed function will be called with each
18226      * node in the tree (or from the startNode). If the function returns true, the node is kept
18227      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18228      * @param {Function} fn The filter function
18229      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18230      */
18231     filterBy : function(fn, scope, startNode){
18232         startNode = startNode || this.tree.root;
18233         if(this.autoClear){
18234             this.clear();
18235         }
18236         var af = this.filtered, rv = this.reverse;
18237         var f = function(n){
18238             if(n == startNode){
18239                 return true;
18240             }
18241             if(af[n.id]){
18242                 return false;
18243             }
18244             var m = fn.call(scope || n, n);
18245             if(!m || rv){
18246                 af[n.id] = n;
18247                 n.ui.hide();
18248                 return false;
18249             }
18250             return true;
18251         };
18252         startNode.cascade(f);
18253         if(this.remove){
18254            for(var id in af){
18255                if(typeof id != "function"){
18256                    var n = af[id];
18257                    if(n && n.parentNode){
18258                        n.parentNode.removeChild(n);
18259                    }
18260                }
18261            }
18262         }
18263     },
18264
18265     /**
18266      * Clears the current filter. Note: with the "remove" option
18267      * set a filter cannot be cleared.
18268      */
18269     clear : function(){
18270         var t = this.tree;
18271         var af = this.filtered;
18272         for(var id in af){
18273             if(typeof id != "function"){
18274                 var n = af[id];
18275                 if(n){
18276                     n.ui.show();
18277                 }
18278             }
18279         }
18280         this.filtered = {};
18281     }
18282 };
18283 /*
18284  * Based on:
18285  * Ext JS Library 1.1.1
18286  * Copyright(c) 2006-2007, Ext JS, LLC.
18287  *
18288  * Originally Released Under LGPL - original licence link has changed is not relivant.
18289  *
18290  * Fork - LGPL
18291  * <script type="text/javascript">
18292  */
18293  
18294
18295 /**
18296  * @class Roo.tree.TreeSorter
18297  * Provides sorting of nodes in a TreePanel
18298  * 
18299  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18300  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18301  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18302  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18303  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18304  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18305  * @constructor
18306  * @param {TreePanel} tree
18307  * @param {Object} config
18308  */
18309 Roo.tree.TreeSorter = function(tree, config){
18310     Roo.apply(this, config);
18311     tree.on("beforechildrenrendered", this.doSort, this);
18312     tree.on("append", this.updateSort, this);
18313     tree.on("insert", this.updateSort, this);
18314     
18315     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18316     var p = this.property || "text";
18317     var sortType = this.sortType;
18318     var fs = this.folderSort;
18319     var cs = this.caseSensitive === true;
18320     var leafAttr = this.leafAttr || 'leaf';
18321
18322     this.sortFn = function(n1, n2){
18323         if(fs){
18324             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18325                 return 1;
18326             }
18327             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18328                 return -1;
18329             }
18330         }
18331         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18332         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18333         if(v1 < v2){
18334                         return dsc ? +1 : -1;
18335                 }else if(v1 > v2){
18336                         return dsc ? -1 : +1;
18337         }else{
18338                 return 0;
18339         }
18340     };
18341 };
18342
18343 Roo.tree.TreeSorter.prototype = {
18344     doSort : function(node){
18345         node.sort(this.sortFn);
18346     },
18347     
18348     compareNodes : function(n1, n2){
18349         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18350     },
18351     
18352     updateSort : function(tree, node){
18353         if(node.childrenRendered){
18354             this.doSort.defer(1, this, [node]);
18355         }
18356     }
18357 };/*
18358  * Based on:
18359  * Ext JS Library 1.1.1
18360  * Copyright(c) 2006-2007, Ext JS, LLC.
18361  *
18362  * Originally Released Under LGPL - original licence link has changed is not relivant.
18363  *
18364  * Fork - LGPL
18365  * <script type="text/javascript">
18366  */
18367
18368 if(Roo.dd.DropZone){
18369     
18370 Roo.tree.TreeDropZone = function(tree, config){
18371     this.allowParentInsert = false;
18372     this.allowContainerDrop = false;
18373     this.appendOnly = false;
18374     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18375     this.tree = tree;
18376     this.lastInsertClass = "x-tree-no-status";
18377     this.dragOverData = {};
18378 };
18379
18380 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18381     ddGroup : "TreeDD",
18382     scroll:  true,
18383     
18384     expandDelay : 1000,
18385     
18386     expandNode : function(node){
18387         if(node.hasChildNodes() && !node.isExpanded()){
18388             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18389         }
18390     },
18391     
18392     queueExpand : function(node){
18393         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18394     },
18395     
18396     cancelExpand : function(){
18397         if(this.expandProcId){
18398             clearTimeout(this.expandProcId);
18399             this.expandProcId = false;
18400         }
18401     },
18402     
18403     isValidDropPoint : function(n, pt, dd, e, data){
18404         if(!n || !data){ return false; }
18405         var targetNode = n.node;
18406         var dropNode = data.node;
18407         // default drop rules
18408         if(!(targetNode && targetNode.isTarget && pt)){
18409             return false;
18410         }
18411         if(pt == "append" && targetNode.allowChildren === false){
18412             return false;
18413         }
18414         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18415             return false;
18416         }
18417         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18418             return false;
18419         }
18420         // reuse the object
18421         var overEvent = this.dragOverData;
18422         overEvent.tree = this.tree;
18423         overEvent.target = targetNode;
18424         overEvent.data = data;
18425         overEvent.point = pt;
18426         overEvent.source = dd;
18427         overEvent.rawEvent = e;
18428         overEvent.dropNode = dropNode;
18429         overEvent.cancel = false;  
18430         var result = this.tree.fireEvent("nodedragover", overEvent);
18431         return overEvent.cancel === false && result !== false;
18432     },
18433     
18434     getDropPoint : function(e, n, dd)
18435     {
18436         var tn = n.node;
18437         if(tn.isRoot){
18438             return tn.allowChildren !== false ? "append" : false; // always append for root
18439         }
18440         var dragEl = n.ddel;
18441         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18442         var y = Roo.lib.Event.getPageY(e);
18443         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18444         
18445         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18446         var noAppend = tn.allowChildren === false;
18447         if(this.appendOnly || tn.parentNode.allowChildren === false){
18448             return noAppend ? false : "append";
18449         }
18450         var noBelow = false;
18451         if(!this.allowParentInsert){
18452             noBelow = tn.hasChildNodes() && tn.isExpanded();
18453         }
18454         var q = (b - t) / (noAppend ? 2 : 3);
18455         if(y >= t && y < (t + q)){
18456             return "above";
18457         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18458             return "below";
18459         }else{
18460             return "append";
18461         }
18462     },
18463     
18464     onNodeEnter : function(n, dd, e, data)
18465     {
18466         this.cancelExpand();
18467     },
18468     
18469     onNodeOver : function(n, dd, e, data)
18470     {
18471        
18472         var pt = this.getDropPoint(e, n, dd);
18473         var node = n.node;
18474         
18475         // auto node expand check
18476         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18477             this.queueExpand(node);
18478         }else if(pt != "append"){
18479             this.cancelExpand();
18480         }
18481         
18482         // set the insert point style on the target node
18483         var returnCls = this.dropNotAllowed;
18484         if(this.isValidDropPoint(n, pt, dd, e, data)){
18485            if(pt){
18486                var el = n.ddel;
18487                var cls;
18488                if(pt == "above"){
18489                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18490                    cls = "x-tree-drag-insert-above";
18491                }else if(pt == "below"){
18492                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18493                    cls = "x-tree-drag-insert-below";
18494                }else{
18495                    returnCls = "x-tree-drop-ok-append";
18496                    cls = "x-tree-drag-append";
18497                }
18498                if(this.lastInsertClass != cls){
18499                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18500                    this.lastInsertClass = cls;
18501                }
18502            }
18503        }
18504        return returnCls;
18505     },
18506     
18507     onNodeOut : function(n, dd, e, data){
18508         
18509         this.cancelExpand();
18510         this.removeDropIndicators(n);
18511     },
18512     
18513     onNodeDrop : function(n, dd, e, data){
18514         var point = this.getDropPoint(e, n, dd);
18515         var targetNode = n.node;
18516         targetNode.ui.startDrop();
18517         if(!this.isValidDropPoint(n, point, dd, e, data)){
18518             targetNode.ui.endDrop();
18519             return false;
18520         }
18521         // first try to find the drop node
18522         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18523         var dropEvent = {
18524             tree : this.tree,
18525             target: targetNode,
18526             data: data,
18527             point: point,
18528             source: dd,
18529             rawEvent: e,
18530             dropNode: dropNode,
18531             cancel: !dropNode   
18532         };
18533         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18534         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18535             targetNode.ui.endDrop();
18536             return false;
18537         }
18538         // allow target changing
18539         targetNode = dropEvent.target;
18540         if(point == "append" && !targetNode.isExpanded()){
18541             targetNode.expand(false, null, function(){
18542                 this.completeDrop(dropEvent);
18543             }.createDelegate(this));
18544         }else{
18545             this.completeDrop(dropEvent);
18546         }
18547         return true;
18548     },
18549     
18550     completeDrop : function(de){
18551         var ns = de.dropNode, p = de.point, t = de.target;
18552         if(!(ns instanceof Array)){
18553             ns = [ns];
18554         }
18555         var n;
18556         for(var i = 0, len = ns.length; i < len; i++){
18557             n = ns[i];
18558             if(p == "above"){
18559                 t.parentNode.insertBefore(n, t);
18560             }else if(p == "below"){
18561                 t.parentNode.insertBefore(n, t.nextSibling);
18562             }else{
18563                 t.appendChild(n);
18564             }
18565         }
18566         n.ui.focus();
18567         if(this.tree.hlDrop){
18568             n.ui.highlight();
18569         }
18570         t.ui.endDrop();
18571         this.tree.fireEvent("nodedrop", de);
18572     },
18573     
18574     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18575         if(this.tree.hlDrop){
18576             dropNode.ui.focus();
18577             dropNode.ui.highlight();
18578         }
18579         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18580     },
18581     
18582     getTree : function(){
18583         return this.tree;
18584     },
18585     
18586     removeDropIndicators : function(n){
18587         if(n && n.ddel){
18588             var el = n.ddel;
18589             Roo.fly(el).removeClass([
18590                     "x-tree-drag-insert-above",
18591                     "x-tree-drag-insert-below",
18592                     "x-tree-drag-append"]);
18593             this.lastInsertClass = "_noclass";
18594         }
18595     },
18596     
18597     beforeDragDrop : function(target, e, id){
18598         this.cancelExpand();
18599         return true;
18600     },
18601     
18602     afterRepair : function(data){
18603         if(data && Roo.enableFx){
18604             data.node.ui.highlight();
18605         }
18606         this.hideProxy();
18607     } 
18608     
18609 });
18610
18611 }
18612 /*
18613  * Based on:
18614  * Ext JS Library 1.1.1
18615  * Copyright(c) 2006-2007, Ext JS, LLC.
18616  *
18617  * Originally Released Under LGPL - original licence link has changed is not relivant.
18618  *
18619  * Fork - LGPL
18620  * <script type="text/javascript">
18621  */
18622  
18623
18624 if(Roo.dd.DragZone){
18625 Roo.tree.TreeDragZone = function(tree, config){
18626     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18627     this.tree = tree;
18628 };
18629
18630 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18631     ddGroup : "TreeDD",
18632    
18633     onBeforeDrag : function(data, e){
18634         var n = data.node;
18635         return n && n.draggable && !n.disabled;
18636     },
18637      
18638     
18639     onInitDrag : function(e){
18640         var data = this.dragData;
18641         this.tree.getSelectionModel().select(data.node);
18642         this.proxy.update("");
18643         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18644         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18645     },
18646     
18647     getRepairXY : function(e, data){
18648         return data.node.ui.getDDRepairXY();
18649     },
18650     
18651     onEndDrag : function(data, e){
18652         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18653         
18654         
18655     },
18656     
18657     onValidDrop : function(dd, e, id){
18658         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18659         this.hideProxy();
18660     },
18661     
18662     beforeInvalidDrop : function(e, id){
18663         // this scrolls the original position back into view
18664         var sm = this.tree.getSelectionModel();
18665         sm.clearSelections();
18666         sm.select(this.dragData.node);
18667     }
18668 });
18669 }/*
18670  * Based on:
18671  * Ext JS Library 1.1.1
18672  * Copyright(c) 2006-2007, Ext JS, LLC.
18673  *
18674  * Originally Released Under LGPL - original licence link has changed is not relivant.
18675  *
18676  * Fork - LGPL
18677  * <script type="text/javascript">
18678  */
18679 /**
18680  * @class Roo.tree.TreeEditor
18681  * @extends Roo.Editor
18682  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18683  * as the editor field.
18684  * @constructor
18685  * @param {Object} config (used to be the tree panel.)
18686  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18687  * 
18688  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18689  * @cfg {Roo.form.TextField|Object} field The field configuration
18690  *
18691  * 
18692  */
18693 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18694     var tree = config;
18695     var field;
18696     if (oldconfig) { // old style..
18697         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18698     } else {
18699         // new style..
18700         tree = config.tree;
18701         config.field = config.field  || {};
18702         config.field.xtype = 'TextField';
18703         field = Roo.factory(config.field, Roo.form);
18704     }
18705     config = config || {};
18706     
18707     
18708     this.addEvents({
18709         /**
18710          * @event beforenodeedit
18711          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18712          * false from the handler of this event.
18713          * @param {Editor} this
18714          * @param {Roo.tree.Node} node 
18715          */
18716         "beforenodeedit" : true
18717     });
18718     
18719     //Roo.log(config);
18720     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18721
18722     this.tree = tree;
18723
18724     tree.on('beforeclick', this.beforeNodeClick, this);
18725     tree.getTreeEl().on('mousedown', this.hide, this);
18726     this.on('complete', this.updateNode, this);
18727     this.on('beforestartedit', this.fitToTree, this);
18728     this.on('startedit', this.bindScroll, this, {delay:10});
18729     this.on('specialkey', this.onSpecialKey, this);
18730 };
18731
18732 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18733     /**
18734      * @cfg {String} alignment
18735      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18736      */
18737     alignment: "l-l",
18738     // inherit
18739     autoSize: false,
18740     /**
18741      * @cfg {Boolean} hideEl
18742      * True to hide the bound element while the editor is displayed (defaults to false)
18743      */
18744     hideEl : false,
18745     /**
18746      * @cfg {String} cls
18747      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18748      */
18749     cls: "x-small-editor x-tree-editor",
18750     /**
18751      * @cfg {Boolean} shim
18752      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18753      */
18754     shim:false,
18755     // inherit
18756     shadow:"frame",
18757     /**
18758      * @cfg {Number} maxWidth
18759      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18760      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18761      * scroll and client offsets into account prior to each edit.
18762      */
18763     maxWidth: 250,
18764
18765     editDelay : 350,
18766
18767     // private
18768     fitToTree : function(ed, el){
18769         var td = this.tree.getTreeEl().dom, nd = el.dom;
18770         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18771             td.scrollLeft = nd.offsetLeft;
18772         }
18773         var w = Math.min(
18774                 this.maxWidth,
18775                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18776         this.setSize(w, '');
18777         
18778         return this.fireEvent('beforenodeedit', this, this.editNode);
18779         
18780     },
18781
18782     // private
18783     triggerEdit : function(node){
18784         this.completeEdit();
18785         this.editNode = node;
18786         this.startEdit(node.ui.textNode, node.text);
18787     },
18788
18789     // private
18790     bindScroll : function(){
18791         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18792     },
18793
18794     // private
18795     beforeNodeClick : function(node, e){
18796         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18797         this.lastClick = new Date();
18798         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18799             e.stopEvent();
18800             this.triggerEdit(node);
18801             return false;
18802         }
18803         return true;
18804     },
18805
18806     // private
18807     updateNode : function(ed, value){
18808         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18809         this.editNode.setText(value);
18810     },
18811
18812     // private
18813     onHide : function(){
18814         Roo.tree.TreeEditor.superclass.onHide.call(this);
18815         if(this.editNode){
18816             this.editNode.ui.focus();
18817         }
18818     },
18819
18820     // private
18821     onSpecialKey : function(field, e){
18822         var k = e.getKey();
18823         if(k == e.ESC){
18824             e.stopEvent();
18825             this.cancelEdit();
18826         }else if(k == e.ENTER && !e.hasModifier()){
18827             e.stopEvent();
18828             this.completeEdit();
18829         }
18830     }
18831 });//<Script type="text/javascript">
18832 /*
18833  * Based on:
18834  * Ext JS Library 1.1.1
18835  * Copyright(c) 2006-2007, Ext JS, LLC.
18836  *
18837  * Originally Released Under LGPL - original licence link has changed is not relivant.
18838  *
18839  * Fork - LGPL
18840  * <script type="text/javascript">
18841  */
18842  
18843 /**
18844  * Not documented??? - probably should be...
18845  */
18846
18847 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18848     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18849     
18850     renderElements : function(n, a, targetNode, bulkRender){
18851         //consel.log("renderElements?");
18852         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18853
18854         var t = n.getOwnerTree();
18855         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18856         
18857         var cols = t.columns;
18858         var bw = t.borderWidth;
18859         var c = cols[0];
18860         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18861          var cb = typeof a.checked == "boolean";
18862         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18863         var colcls = 'x-t-' + tid + '-c0';
18864         var buf = [
18865             '<li class="x-tree-node">',
18866             
18867                 
18868                 '<div class="x-tree-node-el ', a.cls,'">',
18869                     // extran...
18870                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18871                 
18872                 
18873                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18874                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18875                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18876                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18877                            (a.iconCls ? ' '+a.iconCls : ''),
18878                            '" unselectable="on" />',
18879                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18880                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18881                              
18882                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18883                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18884                             '<span unselectable="on" qtip="' + tx + '">',
18885                              tx,
18886                              '</span></a>' ,
18887                     '</div>',
18888                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18889                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18890                  ];
18891         for(var i = 1, len = cols.length; i < len; i++){
18892             c = cols[i];
18893             colcls = 'x-t-' + tid + '-c' +i;
18894             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18895             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18896                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18897                       "</div>");
18898          }
18899          
18900          buf.push(
18901             '</a>',
18902             '<div class="x-clear"></div></div>',
18903             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18904             "</li>");
18905         
18906         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18907             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18908                                 n.nextSibling.ui.getEl(), buf.join(""));
18909         }else{
18910             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18911         }
18912         var el = this.wrap.firstChild;
18913         this.elRow = el;
18914         this.elNode = el.firstChild;
18915         this.ranchor = el.childNodes[1];
18916         this.ctNode = this.wrap.childNodes[1];
18917         var cs = el.firstChild.childNodes;
18918         this.indentNode = cs[0];
18919         this.ecNode = cs[1];
18920         this.iconNode = cs[2];
18921         var index = 3;
18922         if(cb){
18923             this.checkbox = cs[3];
18924             index++;
18925         }
18926         this.anchor = cs[index];
18927         
18928         this.textNode = cs[index].firstChild;
18929         
18930         //el.on("click", this.onClick, this);
18931         //el.on("dblclick", this.onDblClick, this);
18932         
18933         
18934        // console.log(this);
18935     },
18936     initEvents : function(){
18937         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18938         
18939             
18940         var a = this.ranchor;
18941
18942         var el = Roo.get(a);
18943
18944         if(Roo.isOpera){ // opera render bug ignores the CSS
18945             el.setStyle("text-decoration", "none");
18946         }
18947
18948         el.on("click", this.onClick, this);
18949         el.on("dblclick", this.onDblClick, this);
18950         el.on("contextmenu", this.onContextMenu, this);
18951         
18952     },
18953     
18954     /*onSelectedChange : function(state){
18955         if(state){
18956             this.focus();
18957             this.addClass("x-tree-selected");
18958         }else{
18959             //this.blur();
18960             this.removeClass("x-tree-selected");
18961         }
18962     },*/
18963     addClass : function(cls){
18964         if(this.elRow){
18965             Roo.fly(this.elRow).addClass(cls);
18966         }
18967         
18968     },
18969     
18970     
18971     removeClass : function(cls){
18972         if(this.elRow){
18973             Roo.fly(this.elRow).removeClass(cls);
18974         }
18975     }
18976
18977     
18978     
18979 });//<Script type="text/javascript">
18980
18981 /*
18982  * Based on:
18983  * Ext JS Library 1.1.1
18984  * Copyright(c) 2006-2007, Ext JS, LLC.
18985  *
18986  * Originally Released Under LGPL - original licence link has changed is not relivant.
18987  *
18988  * Fork - LGPL
18989  * <script type="text/javascript">
18990  */
18991  
18992
18993 /**
18994  * @class Roo.tree.ColumnTree
18995  * @extends Roo.data.TreePanel
18996  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18997  * @cfg {int} borderWidth  compined right/left border allowance
18998  * @constructor
18999  * @param {String/HTMLElement/Element} el The container element
19000  * @param {Object} config
19001  */
19002 Roo.tree.ColumnTree =  function(el, config)
19003 {
19004    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19005    this.addEvents({
19006         /**
19007         * @event resize
19008         * Fire this event on a container when it resizes
19009         * @param {int} w Width
19010         * @param {int} h Height
19011         */
19012        "resize" : true
19013     });
19014     this.on('resize', this.onResize, this);
19015 };
19016
19017 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19018     //lines:false,
19019     
19020     
19021     borderWidth: Roo.isBorderBox ? 0 : 2, 
19022     headEls : false,
19023     
19024     render : function(){
19025         // add the header.....
19026        
19027         Roo.tree.ColumnTree.superclass.render.apply(this);
19028         
19029         this.el.addClass('x-column-tree');
19030         
19031         this.headers = this.el.createChild(
19032             {cls:'x-tree-headers'},this.innerCt.dom);
19033    
19034         var cols = this.columns, c;
19035         var totalWidth = 0;
19036         this.headEls = [];
19037         var  len = cols.length;
19038         for(var i = 0; i < len; i++){
19039              c = cols[i];
19040              totalWidth += c.width;
19041             this.headEls.push(this.headers.createChild({
19042                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19043                  cn: {
19044                      cls:'x-tree-hd-text',
19045                      html: c.header
19046                  },
19047                  style:'width:'+(c.width-this.borderWidth)+'px;'
19048              }));
19049         }
19050         this.headers.createChild({cls:'x-clear'});
19051         // prevent floats from wrapping when clipped
19052         this.headers.setWidth(totalWidth);
19053         //this.innerCt.setWidth(totalWidth);
19054         this.innerCt.setStyle({ overflow: 'auto' });
19055         this.onResize(this.width, this.height);
19056              
19057         
19058     },
19059     onResize : function(w,h)
19060     {
19061         this.height = h;
19062         this.width = w;
19063         // resize cols..
19064         this.innerCt.setWidth(this.width);
19065         this.innerCt.setHeight(this.height-20);
19066         
19067         // headers...
19068         var cols = this.columns, c;
19069         var totalWidth = 0;
19070         var expEl = false;
19071         var len = cols.length;
19072         for(var i = 0; i < len; i++){
19073             c = cols[i];
19074             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19075                 // it's the expander..
19076                 expEl  = this.headEls[i];
19077                 continue;
19078             }
19079             totalWidth += c.width;
19080             
19081         }
19082         if (expEl) {
19083             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19084         }
19085         this.headers.setWidth(w-20);
19086
19087         
19088         
19089         
19090     }
19091 });
19092 /*
19093  * Based on:
19094  * Ext JS Library 1.1.1
19095  * Copyright(c) 2006-2007, Ext JS, LLC.
19096  *
19097  * Originally Released Under LGPL - original licence link has changed is not relivant.
19098  *
19099  * Fork - LGPL
19100  * <script type="text/javascript">
19101  */
19102  
19103 /**
19104  * @class Roo.menu.Menu
19105  * @extends Roo.util.Observable
19106  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19107  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19108  * @constructor
19109  * Creates a new Menu
19110  * @param {Object} config Configuration options
19111  */
19112 Roo.menu.Menu = function(config){
19113     Roo.apply(this, config);
19114     this.id = this.id || Roo.id();
19115     this.addEvents({
19116         /**
19117          * @event beforeshow
19118          * Fires before this menu is displayed
19119          * @param {Roo.menu.Menu} this
19120          */
19121         beforeshow : true,
19122         /**
19123          * @event beforehide
19124          * Fires before this menu is hidden
19125          * @param {Roo.menu.Menu} this
19126          */
19127         beforehide : true,
19128         /**
19129          * @event show
19130          * Fires after this menu is displayed
19131          * @param {Roo.menu.Menu} this
19132          */
19133         show : true,
19134         /**
19135          * @event hide
19136          * Fires after this menu is hidden
19137          * @param {Roo.menu.Menu} this
19138          */
19139         hide : true,
19140         /**
19141          * @event click
19142          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19143          * @param {Roo.menu.Menu} this
19144          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19145          * @param {Roo.EventObject} e
19146          */
19147         click : true,
19148         /**
19149          * @event mouseover
19150          * Fires when the mouse is hovering over 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         mouseover : true,
19156         /**
19157          * @event mouseout
19158          * Fires when the mouse exits this menu
19159          * @param {Roo.menu.Menu} this
19160          * @param {Roo.EventObject} e
19161          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19162          */
19163         mouseout : true,
19164         /**
19165          * @event itemclick
19166          * Fires when a menu item contained in this menu is clicked
19167          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19168          * @param {Roo.EventObject} e
19169          */
19170         itemclick: true
19171     });
19172     if (this.registerMenu) {
19173         Roo.menu.MenuMgr.register(this);
19174     }
19175     
19176     var mis = this.items;
19177     this.items = new Roo.util.MixedCollection();
19178     if(mis){
19179         this.add.apply(this, mis);
19180     }
19181 };
19182
19183 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19184     /**
19185      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19186      */
19187     minWidth : 120,
19188     /**
19189      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19190      * for bottom-right shadow (defaults to "sides")
19191      */
19192     shadow : "sides",
19193     /**
19194      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19195      * this menu (defaults to "tl-tr?")
19196      */
19197     subMenuAlign : "tl-tr?",
19198     /**
19199      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19200      * relative to its element of origin (defaults to "tl-bl?")
19201      */
19202     defaultAlign : "tl-bl?",
19203     /**
19204      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19205      */
19206     allowOtherMenus : false,
19207     /**
19208      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19209      */
19210     registerMenu : true,
19211
19212     hidden:true,
19213
19214     // private
19215     render : function(){
19216         if(this.el){
19217             return;
19218         }
19219         var el = this.el = new Roo.Layer({
19220             cls: "x-menu",
19221             shadow:this.shadow,
19222             constrain: false,
19223             parentEl: this.parentEl || document.body,
19224             zindex:15000
19225         });
19226
19227         this.keyNav = new Roo.menu.MenuNav(this);
19228
19229         if(this.plain){
19230             el.addClass("x-menu-plain");
19231         }
19232         if(this.cls){
19233             el.addClass(this.cls);
19234         }
19235         // generic focus element
19236         this.focusEl = el.createChild({
19237             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19238         });
19239         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19240         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19241         
19242         ul.on("mouseover", this.onMouseOver, this);
19243         ul.on("mouseout", this.onMouseOut, this);
19244         this.items.each(function(item){
19245             if (item.hidden) {
19246                 return;
19247             }
19248             
19249             var li = document.createElement("li");
19250             li.className = "x-menu-list-item";
19251             ul.dom.appendChild(li);
19252             item.render(li, this);
19253         }, this);
19254         this.ul = ul;
19255         this.autoWidth();
19256     },
19257
19258     // private
19259     autoWidth : function(){
19260         var el = this.el, ul = this.ul;
19261         if(!el){
19262             return;
19263         }
19264         var w = this.width;
19265         if(w){
19266             el.setWidth(w);
19267         }else if(Roo.isIE){
19268             el.setWidth(this.minWidth);
19269             var t = el.dom.offsetWidth; // force recalc
19270             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19271         }
19272     },
19273
19274     // private
19275     delayAutoWidth : function(){
19276         if(this.rendered){
19277             if(!this.awTask){
19278                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19279             }
19280             this.awTask.delay(20);
19281         }
19282     },
19283
19284     // private
19285     findTargetItem : function(e){
19286         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19287         if(t && t.menuItemId){
19288             return this.items.get(t.menuItemId);
19289         }
19290     },
19291
19292     // private
19293     onClick : function(e){
19294         Roo.log("menu.onClick");
19295         var t = this.findTargetItem(e);
19296         if(!t){
19297             return;
19298         }
19299         Roo.log(e);
19300         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19301             if(t == this.activeItem && t.shouldDeactivate(e)){
19302                 this.activeItem.deactivate();
19303                 delete this.activeItem;
19304                 return;
19305             }
19306             if(t.canActivate){
19307                 this.setActiveItem(t, true);
19308             }
19309             return;
19310             
19311             
19312         }
19313         
19314         t.onClick(e);
19315         this.fireEvent("click", this, t, e);
19316     },
19317
19318     // private
19319     setActiveItem : function(item, autoExpand){
19320         if(item != this.activeItem){
19321             if(this.activeItem){
19322                 this.activeItem.deactivate();
19323             }
19324             this.activeItem = item;
19325             item.activate(autoExpand);
19326         }else if(autoExpand){
19327             item.expandMenu();
19328         }
19329     },
19330
19331     // private
19332     tryActivate : function(start, step){
19333         var items = this.items;
19334         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19335             var item = items.get(i);
19336             if(!item.disabled && item.canActivate){
19337                 this.setActiveItem(item, false);
19338                 return item;
19339             }
19340         }
19341         return false;
19342     },
19343
19344     // private
19345     onMouseOver : function(e){
19346         var t;
19347         if(t = this.findTargetItem(e)){
19348             if(t.canActivate && !t.disabled){
19349                 this.setActiveItem(t, true);
19350             }
19351         }
19352         this.fireEvent("mouseover", this, e, t);
19353     },
19354
19355     // private
19356     onMouseOut : function(e){
19357         var t;
19358         if(t = this.findTargetItem(e)){
19359             if(t == this.activeItem && t.shouldDeactivate(e)){
19360                 this.activeItem.deactivate();
19361                 delete this.activeItem;
19362             }
19363         }
19364         this.fireEvent("mouseout", this, e, t);
19365     },
19366
19367     /**
19368      * Read-only.  Returns true if the menu is currently displayed, else false.
19369      * @type Boolean
19370      */
19371     isVisible : function(){
19372         return this.el && !this.hidden;
19373     },
19374
19375     /**
19376      * Displays this menu relative to another element
19377      * @param {String/HTMLElement/Roo.Element} element The element to align to
19378      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19379      * the element (defaults to this.defaultAlign)
19380      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19381      */
19382     show : function(el, pos, parentMenu){
19383         this.parentMenu = parentMenu;
19384         if(!this.el){
19385             this.render();
19386         }
19387         this.fireEvent("beforeshow", this);
19388         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19389     },
19390
19391     /**
19392      * Displays this menu at a specific xy position
19393      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19394      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19395      */
19396     showAt : function(xy, parentMenu, /* private: */_e){
19397         this.parentMenu = parentMenu;
19398         if(!this.el){
19399             this.render();
19400         }
19401         if(_e !== false){
19402             this.fireEvent("beforeshow", this);
19403             xy = this.el.adjustForConstraints(xy);
19404         }
19405         this.el.setXY(xy);
19406         this.el.show();
19407         this.hidden = false;
19408         this.focus();
19409         this.fireEvent("show", this);
19410     },
19411
19412     focus : function(){
19413         if(!this.hidden){
19414             this.doFocus.defer(50, this);
19415         }
19416     },
19417
19418     doFocus : function(){
19419         if(!this.hidden){
19420             this.focusEl.focus();
19421         }
19422     },
19423
19424     /**
19425      * Hides this menu and optionally all parent menus
19426      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19427      */
19428     hide : function(deep){
19429         if(this.el && this.isVisible()){
19430             this.fireEvent("beforehide", this);
19431             if(this.activeItem){
19432                 this.activeItem.deactivate();
19433                 this.activeItem = null;
19434             }
19435             this.el.hide();
19436             this.hidden = true;
19437             this.fireEvent("hide", this);
19438         }
19439         if(deep === true && this.parentMenu){
19440             this.parentMenu.hide(true);
19441         }
19442     },
19443
19444     /**
19445      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19446      * Any of the following are valid:
19447      * <ul>
19448      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19449      * <li>An HTMLElement object which will be converted to a menu item</li>
19450      * <li>A menu item config object that will be created as a new menu item</li>
19451      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19452      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19453      * </ul>
19454      * Usage:
19455      * <pre><code>
19456 // Create the menu
19457 var menu = new Roo.menu.Menu();
19458
19459 // Create a menu item to add by reference
19460 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19461
19462 // Add a bunch of items at once using different methods.
19463 // Only the last item added will be returned.
19464 var item = menu.add(
19465     menuItem,                // add existing item by ref
19466     'Dynamic Item',          // new TextItem
19467     '-',                     // new separator
19468     { text: 'Config Item' }  // new item by config
19469 );
19470 </code></pre>
19471      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19472      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19473      */
19474     add : function(){
19475         var a = arguments, l = a.length, item;
19476         for(var i = 0; i < l; i++){
19477             var el = a[i];
19478             if ((typeof(el) == "object") && el.xtype && el.xns) {
19479                 el = Roo.factory(el, Roo.menu);
19480             }
19481             
19482             if(el.render){ // some kind of Item
19483                 item = this.addItem(el);
19484             }else if(typeof el == "string"){ // string
19485                 if(el == "separator" || el == "-"){
19486                     item = this.addSeparator();
19487                 }else{
19488                     item = this.addText(el);
19489                 }
19490             }else if(el.tagName || el.el){ // element
19491                 item = this.addElement(el);
19492             }else if(typeof el == "object"){ // must be menu item config?
19493                 item = this.addMenuItem(el);
19494             }
19495         }
19496         return item;
19497     },
19498
19499     /**
19500      * Returns this menu's underlying {@link Roo.Element} object
19501      * @return {Roo.Element} The element
19502      */
19503     getEl : function(){
19504         if(!this.el){
19505             this.render();
19506         }
19507         return this.el;
19508     },
19509
19510     /**
19511      * Adds a separator bar to the menu
19512      * @return {Roo.menu.Item} The menu item that was added
19513      */
19514     addSeparator : function(){
19515         return this.addItem(new Roo.menu.Separator());
19516     },
19517
19518     /**
19519      * Adds an {@link Roo.Element} object to the menu
19520      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19521      * @return {Roo.menu.Item} The menu item that was added
19522      */
19523     addElement : function(el){
19524         return this.addItem(new Roo.menu.BaseItem(el));
19525     },
19526
19527     /**
19528      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19529      * @param {Roo.menu.Item} item The menu item to add
19530      * @return {Roo.menu.Item} The menu item that was added
19531      */
19532     addItem : function(item){
19533         this.items.add(item);
19534         if(this.ul){
19535             var li = document.createElement("li");
19536             li.className = "x-menu-list-item";
19537             this.ul.dom.appendChild(li);
19538             item.render(li, this);
19539             this.delayAutoWidth();
19540         }
19541         return item;
19542     },
19543
19544     /**
19545      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19546      * @param {Object} config A MenuItem config object
19547      * @return {Roo.menu.Item} The menu item that was added
19548      */
19549     addMenuItem : function(config){
19550         if(!(config instanceof Roo.menu.Item)){
19551             if(typeof config.checked == "boolean"){ // must be check menu item config?
19552                 config = new Roo.menu.CheckItem(config);
19553             }else{
19554                 config = new Roo.menu.Item(config);
19555             }
19556         }
19557         return this.addItem(config);
19558     },
19559
19560     /**
19561      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19562      * @param {String} text The text to display in the menu item
19563      * @return {Roo.menu.Item} The menu item that was added
19564      */
19565     addText : function(text){
19566         return this.addItem(new Roo.menu.TextItem({ text : text }));
19567     },
19568
19569     /**
19570      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19571      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19572      * @param {Roo.menu.Item} item The menu item to add
19573      * @return {Roo.menu.Item} The menu item that was added
19574      */
19575     insert : function(index, item){
19576         this.items.insert(index, item);
19577         if(this.ul){
19578             var li = document.createElement("li");
19579             li.className = "x-menu-list-item";
19580             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19581             item.render(li, this);
19582             this.delayAutoWidth();
19583         }
19584         return item;
19585     },
19586
19587     /**
19588      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19589      * @param {Roo.menu.Item} item The menu item to remove
19590      */
19591     remove : function(item){
19592         this.items.removeKey(item.id);
19593         item.destroy();
19594     },
19595
19596     /**
19597      * Removes and destroys all items in the menu
19598      */
19599     removeAll : function(){
19600         var f;
19601         while(f = this.items.first()){
19602             this.remove(f);
19603         }
19604     }
19605 });
19606
19607 // MenuNav is a private utility class used internally by the Menu
19608 Roo.menu.MenuNav = function(menu){
19609     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19610     this.scope = this.menu = menu;
19611 };
19612
19613 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19614     doRelay : function(e, h){
19615         var k = e.getKey();
19616         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19617             this.menu.tryActivate(0, 1);
19618             return false;
19619         }
19620         return h.call(this.scope || this, e, this.menu);
19621     },
19622
19623     up : function(e, m){
19624         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19625             m.tryActivate(m.items.length-1, -1);
19626         }
19627     },
19628
19629     down : function(e, m){
19630         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19631             m.tryActivate(0, 1);
19632         }
19633     },
19634
19635     right : function(e, m){
19636         if(m.activeItem){
19637             m.activeItem.expandMenu(true);
19638         }
19639     },
19640
19641     left : function(e, m){
19642         m.hide();
19643         if(m.parentMenu && m.parentMenu.activeItem){
19644             m.parentMenu.activeItem.activate();
19645         }
19646     },
19647
19648     enter : function(e, m){
19649         if(m.activeItem){
19650             e.stopPropagation();
19651             m.activeItem.onClick(e);
19652             m.fireEvent("click", this, m.activeItem);
19653             return true;
19654         }
19655     }
19656 });/*
19657  * Based on:
19658  * Ext JS Library 1.1.1
19659  * Copyright(c) 2006-2007, Ext JS, LLC.
19660  *
19661  * Originally Released Under LGPL - original licence link has changed is not relivant.
19662  *
19663  * Fork - LGPL
19664  * <script type="text/javascript">
19665  */
19666  
19667 /**
19668  * @class Roo.menu.MenuMgr
19669  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19670  * @singleton
19671  */
19672 Roo.menu.MenuMgr = function(){
19673    var menus, active, groups = {}, attached = false, lastShow = new Date();
19674
19675    // private - called when first menu is created
19676    function init(){
19677        menus = {};
19678        active = new Roo.util.MixedCollection();
19679        Roo.get(document).addKeyListener(27, function(){
19680            if(active.length > 0){
19681                hideAll();
19682            }
19683        });
19684    }
19685
19686    // private
19687    function hideAll(){
19688        if(active && active.length > 0){
19689            var c = active.clone();
19690            c.each(function(m){
19691                m.hide();
19692            });
19693        }
19694    }
19695
19696    // private
19697    function onHide(m){
19698        active.remove(m);
19699        if(active.length < 1){
19700            Roo.get(document).un("mousedown", onMouseDown);
19701            attached = false;
19702        }
19703    }
19704
19705    // private
19706    function onShow(m){
19707        var last = active.last();
19708        lastShow = new Date();
19709        active.add(m);
19710        if(!attached){
19711            Roo.get(document).on("mousedown", onMouseDown);
19712            attached = true;
19713        }
19714        if(m.parentMenu){
19715           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19716           m.parentMenu.activeChild = m;
19717        }else if(last && last.isVisible()){
19718           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19719        }
19720    }
19721
19722    // private
19723    function onBeforeHide(m){
19724        if(m.activeChild){
19725            m.activeChild.hide();
19726        }
19727        if(m.autoHideTimer){
19728            clearTimeout(m.autoHideTimer);
19729            delete m.autoHideTimer;
19730        }
19731    }
19732
19733    // private
19734    function onBeforeShow(m){
19735        var pm = m.parentMenu;
19736        if(!pm && !m.allowOtherMenus){
19737            hideAll();
19738        }else if(pm && pm.activeChild && active != m){
19739            pm.activeChild.hide();
19740        }
19741    }
19742
19743    // private
19744    function onMouseDown(e){
19745        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19746            hideAll();
19747        }
19748    }
19749
19750    // private
19751    function onBeforeCheck(mi, state){
19752        if(state){
19753            var g = groups[mi.group];
19754            for(var i = 0, l = g.length; i < l; i++){
19755                if(g[i] != mi){
19756                    g[i].setChecked(false);
19757                }
19758            }
19759        }
19760    }
19761
19762    return {
19763
19764        /**
19765         * Hides all menus that are currently visible
19766         */
19767        hideAll : function(){
19768             hideAll();  
19769        },
19770
19771        // private
19772        register : function(menu){
19773            if(!menus){
19774                init();
19775            }
19776            menus[menu.id] = menu;
19777            menu.on("beforehide", onBeforeHide);
19778            menu.on("hide", onHide);
19779            menu.on("beforeshow", onBeforeShow);
19780            menu.on("show", onShow);
19781            var g = menu.group;
19782            if(g && menu.events["checkchange"]){
19783                if(!groups[g]){
19784                    groups[g] = [];
19785                }
19786                groups[g].push(menu);
19787                menu.on("checkchange", onCheck);
19788            }
19789        },
19790
19791         /**
19792          * Returns a {@link Roo.menu.Menu} object
19793          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19794          * be used to generate and return a new Menu instance.
19795          */
19796        get : function(menu){
19797            if(typeof menu == "string"){ // menu id
19798                return menus[menu];
19799            }else if(menu.events){  // menu instance
19800                return menu;
19801            }else if(typeof menu.length == 'number'){ // array of menu items?
19802                return new Roo.menu.Menu({items:menu});
19803            }else{ // otherwise, must be a config
19804                return new Roo.menu.Menu(menu);
19805            }
19806        },
19807
19808        // private
19809        unregister : function(menu){
19810            delete menus[menu.id];
19811            menu.un("beforehide", onBeforeHide);
19812            menu.un("hide", onHide);
19813            menu.un("beforeshow", onBeforeShow);
19814            menu.un("show", onShow);
19815            var g = menu.group;
19816            if(g && menu.events["checkchange"]){
19817                groups[g].remove(menu);
19818                menu.un("checkchange", onCheck);
19819            }
19820        },
19821
19822        // private
19823        registerCheckable : function(menuItem){
19824            var g = menuItem.group;
19825            if(g){
19826                if(!groups[g]){
19827                    groups[g] = [];
19828                }
19829                groups[g].push(menuItem);
19830                menuItem.on("beforecheckchange", onBeforeCheck);
19831            }
19832        },
19833
19834        // private
19835        unregisterCheckable : function(menuItem){
19836            var g = menuItem.group;
19837            if(g){
19838                groups[g].remove(menuItem);
19839                menuItem.un("beforecheckchange", onBeforeCheck);
19840            }
19841        }
19842    };
19843 }();/*
19844  * Based on:
19845  * Ext JS Library 1.1.1
19846  * Copyright(c) 2006-2007, Ext JS, LLC.
19847  *
19848  * Originally Released Under LGPL - original licence link has changed is not relivant.
19849  *
19850  * Fork - LGPL
19851  * <script type="text/javascript">
19852  */
19853  
19854
19855 /**
19856  * @class Roo.menu.BaseItem
19857  * @extends Roo.Component
19858  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19859  * management and base configuration options shared by all menu components.
19860  * @constructor
19861  * Creates a new BaseItem
19862  * @param {Object} config Configuration options
19863  */
19864 Roo.menu.BaseItem = function(config){
19865     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19866
19867     this.addEvents({
19868         /**
19869          * @event click
19870          * Fires when this item is clicked
19871          * @param {Roo.menu.BaseItem} this
19872          * @param {Roo.EventObject} e
19873          */
19874         click: true,
19875         /**
19876          * @event activate
19877          * Fires when this item is activated
19878          * @param {Roo.menu.BaseItem} this
19879          */
19880         activate : true,
19881         /**
19882          * @event deactivate
19883          * Fires when this item is deactivated
19884          * @param {Roo.menu.BaseItem} this
19885          */
19886         deactivate : true
19887     });
19888
19889     if(this.handler){
19890         this.on("click", this.handler, this.scope, true);
19891     }
19892 };
19893
19894 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19895     /**
19896      * @cfg {Function} handler
19897      * A function that will handle the click event of this menu item (defaults to undefined)
19898      */
19899     /**
19900      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19901      */
19902     canActivate : false,
19903     
19904      /**
19905      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19906      */
19907     hidden: false,
19908     
19909     /**
19910      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19911      */
19912     activeClass : "x-menu-item-active",
19913     /**
19914      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19915      */
19916     hideOnClick : true,
19917     /**
19918      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19919      */
19920     hideDelay : 100,
19921
19922     // private
19923     ctype: "Roo.menu.BaseItem",
19924
19925     // private
19926     actionMode : "container",
19927
19928     // private
19929     render : function(container, parentMenu){
19930         this.parentMenu = parentMenu;
19931         Roo.menu.BaseItem.superclass.render.call(this, container);
19932         this.container.menuItemId = this.id;
19933     },
19934
19935     // private
19936     onRender : function(container, position){
19937         this.el = Roo.get(this.el);
19938         container.dom.appendChild(this.el.dom);
19939     },
19940
19941     // private
19942     onClick : function(e){
19943         if(!this.disabled && this.fireEvent("click", this, e) !== false
19944                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19945             this.handleClick(e);
19946         }else{
19947             e.stopEvent();
19948         }
19949     },
19950
19951     // private
19952     activate : function(){
19953         if(this.disabled){
19954             return false;
19955         }
19956         var li = this.container;
19957         li.addClass(this.activeClass);
19958         this.region = li.getRegion().adjust(2, 2, -2, -2);
19959         this.fireEvent("activate", this);
19960         return true;
19961     },
19962
19963     // private
19964     deactivate : function(){
19965         this.container.removeClass(this.activeClass);
19966         this.fireEvent("deactivate", this);
19967     },
19968
19969     // private
19970     shouldDeactivate : function(e){
19971         return !this.region || !this.region.contains(e.getPoint());
19972     },
19973
19974     // private
19975     handleClick : function(e){
19976         if(this.hideOnClick){
19977             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19978         }
19979     },
19980
19981     // private
19982     expandMenu : function(autoActivate){
19983         // do nothing
19984     },
19985
19986     // private
19987     hideMenu : function(){
19988         // do nothing
19989     }
19990 });/*
19991  * Based on:
19992  * Ext JS Library 1.1.1
19993  * Copyright(c) 2006-2007, Ext JS, LLC.
19994  *
19995  * Originally Released Under LGPL - original licence link has changed is not relivant.
19996  *
19997  * Fork - LGPL
19998  * <script type="text/javascript">
19999  */
20000  
20001 /**
20002  * @class Roo.menu.Adapter
20003  * @extends Roo.menu.BaseItem
20004  * 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.
20005  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20006  * @constructor
20007  * Creates a new Adapter
20008  * @param {Object} config Configuration options
20009  */
20010 Roo.menu.Adapter = function(component, config){
20011     Roo.menu.Adapter.superclass.constructor.call(this, config);
20012     this.component = component;
20013 };
20014 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20015     // private
20016     canActivate : true,
20017
20018     // private
20019     onRender : function(container, position){
20020         this.component.render(container);
20021         this.el = this.component.getEl();
20022     },
20023
20024     // private
20025     activate : function(){
20026         if(this.disabled){
20027             return false;
20028         }
20029         this.component.focus();
20030         this.fireEvent("activate", this);
20031         return true;
20032     },
20033
20034     // private
20035     deactivate : function(){
20036         this.fireEvent("deactivate", this);
20037     },
20038
20039     // private
20040     disable : function(){
20041         this.component.disable();
20042         Roo.menu.Adapter.superclass.disable.call(this);
20043     },
20044
20045     // private
20046     enable : function(){
20047         this.component.enable();
20048         Roo.menu.Adapter.superclass.enable.call(this);
20049     }
20050 });/*
20051  * Based on:
20052  * Ext JS Library 1.1.1
20053  * Copyright(c) 2006-2007, Ext JS, LLC.
20054  *
20055  * Originally Released Under LGPL - original licence link has changed is not relivant.
20056  *
20057  * Fork - LGPL
20058  * <script type="text/javascript">
20059  */
20060
20061 /**
20062  * @class Roo.menu.TextItem
20063  * @extends Roo.menu.BaseItem
20064  * Adds a static text string to a menu, usually used as either a heading or group separator.
20065  * Note: old style constructor with text is still supported.
20066  * 
20067  * @constructor
20068  * Creates a new TextItem
20069  * @param {Object} cfg Configuration
20070  */
20071 Roo.menu.TextItem = function(cfg){
20072     if (typeof(cfg) == 'string') {
20073         this.text = cfg;
20074     } else {
20075         Roo.apply(this,cfg);
20076     }
20077     
20078     Roo.menu.TextItem.superclass.constructor.call(this);
20079 };
20080
20081 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20082     /**
20083      * @cfg {Boolean} text Text to show on item.
20084      */
20085     text : '',
20086     
20087     /**
20088      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20089      */
20090     hideOnClick : false,
20091     /**
20092      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20093      */
20094     itemCls : "x-menu-text",
20095
20096     // private
20097     onRender : function(){
20098         var s = document.createElement("span");
20099         s.className = this.itemCls;
20100         s.innerHTML = this.text;
20101         this.el = s;
20102         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20103     }
20104 });/*
20105  * Based on:
20106  * Ext JS Library 1.1.1
20107  * Copyright(c) 2006-2007, Ext JS, LLC.
20108  *
20109  * Originally Released Under LGPL - original licence link has changed is not relivant.
20110  *
20111  * Fork - LGPL
20112  * <script type="text/javascript">
20113  */
20114
20115 /**
20116  * @class Roo.menu.Separator
20117  * @extends Roo.menu.BaseItem
20118  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20119  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20120  * @constructor
20121  * @param {Object} config Configuration options
20122  */
20123 Roo.menu.Separator = function(config){
20124     Roo.menu.Separator.superclass.constructor.call(this, config);
20125 };
20126
20127 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20128     /**
20129      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20130      */
20131     itemCls : "x-menu-sep",
20132     /**
20133      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20134      */
20135     hideOnClick : false,
20136
20137     // private
20138     onRender : function(li){
20139         var s = document.createElement("span");
20140         s.className = this.itemCls;
20141         s.innerHTML = "&#160;";
20142         this.el = s;
20143         li.addClass("x-menu-sep-li");
20144         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20145     }
20146 });/*
20147  * Based on:
20148  * Ext JS Library 1.1.1
20149  * Copyright(c) 2006-2007, Ext JS, LLC.
20150  *
20151  * Originally Released Under LGPL - original licence link has changed is not relivant.
20152  *
20153  * Fork - LGPL
20154  * <script type="text/javascript">
20155  */
20156 /**
20157  * @class Roo.menu.Item
20158  * @extends Roo.menu.BaseItem
20159  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20160  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20161  * activation and click handling.
20162  * @constructor
20163  * Creates a new Item
20164  * @param {Object} config Configuration options
20165  */
20166 Roo.menu.Item = function(config){
20167     Roo.menu.Item.superclass.constructor.call(this, config);
20168     if(this.menu){
20169         this.menu = Roo.menu.MenuMgr.get(this.menu);
20170     }
20171 };
20172 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20173     
20174     /**
20175      * @cfg {String} text
20176      * The text to show on the menu item.
20177      */
20178     text: '',
20179      /**
20180      * @cfg {String} HTML to render in menu
20181      * The text to show on the menu item (HTML version).
20182      */
20183     html: '',
20184     /**
20185      * @cfg {String} icon
20186      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20187      */
20188     icon: undefined,
20189     /**
20190      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20191      */
20192     itemCls : "x-menu-item",
20193     /**
20194      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20195      */
20196     canActivate : true,
20197     /**
20198      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20199      */
20200     showDelay: 200,
20201     // doc'd in BaseItem
20202     hideDelay: 200,
20203
20204     // private
20205     ctype: "Roo.menu.Item",
20206     
20207     // private
20208     onRender : function(container, position){
20209         var el = document.createElement("a");
20210         el.hideFocus = true;
20211         el.unselectable = "on";
20212         el.href = this.href || "#";
20213         if(this.hrefTarget){
20214             el.target = this.hrefTarget;
20215         }
20216         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20217         
20218         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20219         
20220         el.innerHTML = String.format(
20221                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20222                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20223         this.el = el;
20224         Roo.menu.Item.superclass.onRender.call(this, container, position);
20225     },
20226
20227     /**
20228      * Sets the text to display in this menu item
20229      * @param {String} text The text to display
20230      * @param {Boolean} isHTML true to indicate text is pure html.
20231      */
20232     setText : function(text, isHTML){
20233         if (isHTML) {
20234             this.html = text;
20235         } else {
20236             this.text = text;
20237             this.html = '';
20238         }
20239         if(this.rendered){
20240             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20241      
20242             this.el.update(String.format(
20243                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20244                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20245             this.parentMenu.autoWidth();
20246         }
20247     },
20248
20249     // private
20250     handleClick : function(e){
20251         if(!this.href){ // if no link defined, stop the event automatically
20252             e.stopEvent();
20253         }
20254         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20255     },
20256
20257     // private
20258     activate : function(autoExpand){
20259         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20260             this.focus();
20261             if(autoExpand){
20262                 this.expandMenu();
20263             }
20264         }
20265         return true;
20266     },
20267
20268     // private
20269     shouldDeactivate : function(e){
20270         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20271             if(this.menu && this.menu.isVisible()){
20272                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20273             }
20274             return true;
20275         }
20276         return false;
20277     },
20278
20279     // private
20280     deactivate : function(){
20281         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20282         this.hideMenu();
20283     },
20284
20285     // private
20286     expandMenu : function(autoActivate){
20287         if(!this.disabled && this.menu){
20288             clearTimeout(this.hideTimer);
20289             delete this.hideTimer;
20290             if(!this.menu.isVisible() && !this.showTimer){
20291                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20292             }else if (this.menu.isVisible() && autoActivate){
20293                 this.menu.tryActivate(0, 1);
20294             }
20295         }
20296     },
20297
20298     // private
20299     deferExpand : function(autoActivate){
20300         delete this.showTimer;
20301         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20302         if(autoActivate){
20303             this.menu.tryActivate(0, 1);
20304         }
20305     },
20306
20307     // private
20308     hideMenu : function(){
20309         clearTimeout(this.showTimer);
20310         delete this.showTimer;
20311         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20312             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20313         }
20314     },
20315
20316     // private
20317     deferHide : function(){
20318         delete this.hideTimer;
20319         this.menu.hide();
20320     }
20321 });/*
20322  * Based on:
20323  * Ext JS Library 1.1.1
20324  * Copyright(c) 2006-2007, Ext JS, LLC.
20325  *
20326  * Originally Released Under LGPL - original licence link has changed is not relivant.
20327  *
20328  * Fork - LGPL
20329  * <script type="text/javascript">
20330  */
20331  
20332 /**
20333  * @class Roo.menu.CheckItem
20334  * @extends Roo.menu.Item
20335  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20336  * @constructor
20337  * Creates a new CheckItem
20338  * @param {Object} config Configuration options
20339  */
20340 Roo.menu.CheckItem = function(config){
20341     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20342     this.addEvents({
20343         /**
20344          * @event beforecheckchange
20345          * Fires before the checked value is set, providing an opportunity to cancel if needed
20346          * @param {Roo.menu.CheckItem} this
20347          * @param {Boolean} checked The new checked value that will be set
20348          */
20349         "beforecheckchange" : true,
20350         /**
20351          * @event checkchange
20352          * Fires after the checked value has been set
20353          * @param {Roo.menu.CheckItem} this
20354          * @param {Boolean} checked The checked value that was set
20355          */
20356         "checkchange" : true
20357     });
20358     if(this.checkHandler){
20359         this.on('checkchange', this.checkHandler, this.scope);
20360     }
20361 };
20362 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20363     /**
20364      * @cfg {String} group
20365      * All check items with the same group name will automatically be grouped into a single-select
20366      * radio button group (defaults to '')
20367      */
20368     /**
20369      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20370      */
20371     itemCls : "x-menu-item x-menu-check-item",
20372     /**
20373      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20374      */
20375     groupClass : "x-menu-group-item",
20376
20377     /**
20378      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20379      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20380      * initialized with checked = true will be rendered as checked.
20381      */
20382     checked: false,
20383
20384     // private
20385     ctype: "Roo.menu.CheckItem",
20386
20387     // private
20388     onRender : function(c){
20389         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20390         if(this.group){
20391             this.el.addClass(this.groupClass);
20392         }
20393         Roo.menu.MenuMgr.registerCheckable(this);
20394         if(this.checked){
20395             this.checked = false;
20396             this.setChecked(true, true);
20397         }
20398     },
20399
20400     // private
20401     destroy : function(){
20402         if(this.rendered){
20403             Roo.menu.MenuMgr.unregisterCheckable(this);
20404         }
20405         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20406     },
20407
20408     /**
20409      * Set the checked state of this item
20410      * @param {Boolean} checked The new checked value
20411      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20412      */
20413     setChecked : function(state, suppressEvent){
20414         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20415             if(this.container){
20416                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20417             }
20418             this.checked = state;
20419             if(suppressEvent !== true){
20420                 this.fireEvent("checkchange", this, state);
20421             }
20422         }
20423     },
20424
20425     // private
20426     handleClick : function(e){
20427        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20428            this.setChecked(!this.checked);
20429        }
20430        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20431     }
20432 });/*
20433  * Based on:
20434  * Ext JS Library 1.1.1
20435  * Copyright(c) 2006-2007, Ext JS, LLC.
20436  *
20437  * Originally Released Under LGPL - original licence link has changed is not relivant.
20438  *
20439  * Fork - LGPL
20440  * <script type="text/javascript">
20441  */
20442  
20443 /**
20444  * @class Roo.menu.DateItem
20445  * @extends Roo.menu.Adapter
20446  * A menu item that wraps the {@link Roo.DatPicker} component.
20447  * @constructor
20448  * Creates a new DateItem
20449  * @param {Object} config Configuration options
20450  */
20451 Roo.menu.DateItem = function(config){
20452     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20453     /** The Roo.DatePicker object @type Roo.DatePicker */
20454     this.picker = this.component;
20455     this.addEvents({select: true});
20456     
20457     this.picker.on("render", function(picker){
20458         picker.getEl().swallowEvent("click");
20459         picker.container.addClass("x-menu-date-item");
20460     });
20461
20462     this.picker.on("select", this.onSelect, this);
20463 };
20464
20465 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20466     // private
20467     onSelect : function(picker, date){
20468         this.fireEvent("select", this, date, picker);
20469         Roo.menu.DateItem.superclass.handleClick.call(this);
20470     }
20471 });/*
20472  * Based on:
20473  * Ext JS Library 1.1.1
20474  * Copyright(c) 2006-2007, Ext JS, LLC.
20475  *
20476  * Originally Released Under LGPL - original licence link has changed is not relivant.
20477  *
20478  * Fork - LGPL
20479  * <script type="text/javascript">
20480  */
20481  
20482 /**
20483  * @class Roo.menu.ColorItem
20484  * @extends Roo.menu.Adapter
20485  * A menu item that wraps the {@link Roo.ColorPalette} component.
20486  * @constructor
20487  * Creates a new ColorItem
20488  * @param {Object} config Configuration options
20489  */
20490 Roo.menu.ColorItem = function(config){
20491     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20492     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20493     this.palette = this.component;
20494     this.relayEvents(this.palette, ["select"]);
20495     if(this.selectHandler){
20496         this.on('select', this.selectHandler, this.scope);
20497     }
20498 };
20499 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20500  * Based on:
20501  * Ext JS Library 1.1.1
20502  * Copyright(c) 2006-2007, Ext JS, LLC.
20503  *
20504  * Originally Released Under LGPL - original licence link has changed is not relivant.
20505  *
20506  * Fork - LGPL
20507  * <script type="text/javascript">
20508  */
20509  
20510
20511 /**
20512  * @class Roo.menu.DateMenu
20513  * @extends Roo.menu.Menu
20514  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20515  * @constructor
20516  * Creates a new DateMenu
20517  * @param {Object} config Configuration options
20518  */
20519 Roo.menu.DateMenu = function(config){
20520     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20521     this.plain = true;
20522     var di = new Roo.menu.DateItem(config);
20523     this.add(di);
20524     /**
20525      * The {@link Roo.DatePicker} instance for this DateMenu
20526      * @type DatePicker
20527      */
20528     this.picker = di.picker;
20529     /**
20530      * @event select
20531      * @param {DatePicker} picker
20532      * @param {Date} date
20533      */
20534     this.relayEvents(di, ["select"]);
20535     this.on('beforeshow', function(){
20536         if(this.picker){
20537             this.picker.hideMonthPicker(false);
20538         }
20539     }, this);
20540 };
20541 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20542     cls:'x-date-menu'
20543 });/*
20544  * Based on:
20545  * Ext JS Library 1.1.1
20546  * Copyright(c) 2006-2007, Ext JS, LLC.
20547  *
20548  * Originally Released Under LGPL - original licence link has changed is not relivant.
20549  *
20550  * Fork - LGPL
20551  * <script type="text/javascript">
20552  */
20553  
20554
20555 /**
20556  * @class Roo.menu.ColorMenu
20557  * @extends Roo.menu.Menu
20558  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20559  * @constructor
20560  * Creates a new ColorMenu
20561  * @param {Object} config Configuration options
20562  */
20563 Roo.menu.ColorMenu = function(config){
20564     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20565     this.plain = true;
20566     var ci = new Roo.menu.ColorItem(config);
20567     this.add(ci);
20568     /**
20569      * The {@link Roo.ColorPalette} instance for this ColorMenu
20570      * @type ColorPalette
20571      */
20572     this.palette = ci.palette;
20573     /**
20574      * @event select
20575      * @param {ColorPalette} palette
20576      * @param {String} color
20577      */
20578     this.relayEvents(ci, ["select"]);
20579 };
20580 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20581  * Based on:
20582  * Ext JS Library 1.1.1
20583  * Copyright(c) 2006-2007, Ext JS, LLC.
20584  *
20585  * Originally Released Under LGPL - original licence link has changed is not relivant.
20586  *
20587  * Fork - LGPL
20588  * <script type="text/javascript">
20589  */
20590  
20591 /**
20592  * @class Roo.form.Field
20593  * @extends Roo.BoxComponent
20594  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20595  * @constructor
20596  * Creates a new Field
20597  * @param {Object} config Configuration options
20598  */
20599 Roo.form.Field = function(config){
20600     Roo.form.Field.superclass.constructor.call(this, config);
20601 };
20602
20603 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20604     /**
20605      * @cfg {String} fieldLabel Label to use when rendering a form.
20606      */
20607        /**
20608      * @cfg {String} qtip Mouse over tip
20609      */
20610      
20611     /**
20612      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20613      */
20614     invalidClass : "x-form-invalid",
20615     /**
20616      * @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")
20617      */
20618     invalidText : "The value in this field is invalid",
20619     /**
20620      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20621      */
20622     focusClass : "x-form-focus",
20623     /**
20624      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20625       automatic validation (defaults to "keyup").
20626      */
20627     validationEvent : "keyup",
20628     /**
20629      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20630      */
20631     validateOnBlur : true,
20632     /**
20633      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20634      */
20635     validationDelay : 250,
20636     /**
20637      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20638      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20639      */
20640     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20641     /**
20642      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20643      */
20644     fieldClass : "x-form-field",
20645     /**
20646      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20647      *<pre>
20648 Value         Description
20649 -----------   ----------------------------------------------------------------------
20650 qtip          Display a quick tip when the user hovers over the field
20651 title         Display a default browser title attribute popup
20652 under         Add a block div beneath the field containing the error text
20653 side          Add an error icon to the right of the field with a popup on hover
20654 [element id]  Add the error text directly to the innerHTML of the specified element
20655 </pre>
20656      */
20657     msgTarget : 'qtip',
20658     /**
20659      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20660      */
20661     msgFx : 'normal',
20662
20663     /**
20664      * @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.
20665      */
20666     readOnly : false,
20667
20668     /**
20669      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20670      */
20671     disabled : false,
20672
20673     /**
20674      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20675      */
20676     inputType : undefined,
20677     
20678     /**
20679      * @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).
20680          */
20681         tabIndex : undefined,
20682         
20683     // private
20684     isFormField : true,
20685
20686     // private
20687     hasFocus : false,
20688     /**
20689      * @property {Roo.Element} fieldEl
20690      * Element Containing the rendered Field (with label etc.)
20691      */
20692     /**
20693      * @cfg {Mixed} value A value to initialize this field with.
20694      */
20695     value : undefined,
20696
20697     /**
20698      * @cfg {String} name The field's HTML name attribute.
20699      */
20700     /**
20701      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20702      */
20703
20704         // private ??
20705         initComponent : function(){
20706         Roo.form.Field.superclass.initComponent.call(this);
20707         this.addEvents({
20708             /**
20709              * @event focus
20710              * Fires when this field receives input focus.
20711              * @param {Roo.form.Field} this
20712              */
20713             focus : true,
20714             /**
20715              * @event blur
20716              * Fires when this field loses input focus.
20717              * @param {Roo.form.Field} this
20718              */
20719             blur : true,
20720             /**
20721              * @event specialkey
20722              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20723              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20724              * @param {Roo.form.Field} this
20725              * @param {Roo.EventObject} e The event object
20726              */
20727             specialkey : true,
20728             /**
20729              * @event change
20730              * Fires just before the field blurs if the field value has changed.
20731              * @param {Roo.form.Field} this
20732              * @param {Mixed} newValue The new value
20733              * @param {Mixed} oldValue The original value
20734              */
20735             change : true,
20736             /**
20737              * @event invalid
20738              * Fires after the field has been marked as invalid.
20739              * @param {Roo.form.Field} this
20740              * @param {String} msg The validation message
20741              */
20742             invalid : true,
20743             /**
20744              * @event valid
20745              * Fires after the field has been validated with no errors.
20746              * @param {Roo.form.Field} this
20747              */
20748             valid : true,
20749              /**
20750              * @event keyup
20751              * Fires after the key up
20752              * @param {Roo.form.Field} this
20753              * @param {Roo.EventObject}  e The event Object
20754              */
20755             keyup : true
20756         });
20757     },
20758
20759     /**
20760      * Returns the name attribute of the field if available
20761      * @return {String} name The field name
20762      */
20763     getName: function(){
20764          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20765     },
20766
20767     // private
20768     onRender : function(ct, position){
20769         Roo.form.Field.superclass.onRender.call(this, ct, position);
20770         if(!this.el){
20771             var cfg = this.getAutoCreate();
20772             if(!cfg.name){
20773                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20774             }
20775             if (!cfg.name.length) {
20776                 delete cfg.name;
20777             }
20778             if(this.inputType){
20779                 cfg.type = this.inputType;
20780             }
20781             this.el = ct.createChild(cfg, position);
20782         }
20783         var type = this.el.dom.type;
20784         if(type){
20785             if(type == 'password'){
20786                 type = 'text';
20787             }
20788             this.el.addClass('x-form-'+type);
20789         }
20790         if(this.readOnly){
20791             this.el.dom.readOnly = true;
20792         }
20793         if(this.tabIndex !== undefined){
20794             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20795         }
20796
20797         this.el.addClass([this.fieldClass, this.cls]);
20798         this.initValue();
20799     },
20800
20801     /**
20802      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20803      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20804      * @return {Roo.form.Field} this
20805      */
20806     applyTo : function(target){
20807         this.allowDomMove = false;
20808         this.el = Roo.get(target);
20809         this.render(this.el.dom.parentNode);
20810         return this;
20811     },
20812
20813     // private
20814     initValue : function(){
20815         if(this.value !== undefined){
20816             this.setValue(this.value);
20817         }else if(this.el.dom.value.length > 0){
20818             this.setValue(this.el.dom.value);
20819         }
20820     },
20821
20822     /**
20823      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20824      */
20825     isDirty : function() {
20826         if(this.disabled) {
20827             return false;
20828         }
20829         return String(this.getValue()) !== String(this.originalValue);
20830     },
20831
20832     // private
20833     afterRender : function(){
20834         Roo.form.Field.superclass.afterRender.call(this);
20835         this.initEvents();
20836     },
20837
20838     // private
20839     fireKey : function(e){
20840         //Roo.log('field ' + e.getKey());
20841         if(e.isNavKeyPress()){
20842             this.fireEvent("specialkey", this, e);
20843         }
20844     },
20845
20846     /**
20847      * Resets the current field value to the originally loaded value and clears any validation messages
20848      */
20849     reset : function(){
20850         this.setValue(this.resetValue);
20851         this.clearInvalid();
20852     },
20853
20854     // private
20855     initEvents : function(){
20856         // safari killled keypress - so keydown is now used..
20857         this.el.on("keydown" , this.fireKey,  this);
20858         this.el.on("focus", this.onFocus,  this);
20859         this.el.on("blur", this.onBlur,  this);
20860         this.el.relayEvent('keyup', this);
20861
20862         // reference to original value for reset
20863         this.originalValue = this.getValue();
20864         this.resetValue =  this.getValue();
20865     },
20866
20867     // private
20868     onFocus : function(){
20869         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20870             this.el.addClass(this.focusClass);
20871         }
20872         if(!this.hasFocus){
20873             this.hasFocus = true;
20874             this.startValue = this.getValue();
20875             this.fireEvent("focus", this);
20876         }
20877     },
20878
20879     beforeBlur : Roo.emptyFn,
20880
20881     // private
20882     onBlur : function(){
20883         this.beforeBlur();
20884         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20885             this.el.removeClass(this.focusClass);
20886         }
20887         this.hasFocus = false;
20888         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20889             this.validate();
20890         }
20891         var v = this.getValue();
20892         if(String(v) !== String(this.startValue)){
20893             this.fireEvent('change', this, v, this.startValue);
20894         }
20895         this.fireEvent("blur", this);
20896     },
20897
20898     /**
20899      * Returns whether or not the field value is currently valid
20900      * @param {Boolean} preventMark True to disable marking the field invalid
20901      * @return {Boolean} True if the value is valid, else false
20902      */
20903     isValid : function(preventMark){
20904         if(this.disabled){
20905             return true;
20906         }
20907         var restore = this.preventMark;
20908         this.preventMark = preventMark === true;
20909         var v = this.validateValue(this.processValue(this.getRawValue()));
20910         this.preventMark = restore;
20911         return v;
20912     },
20913
20914     /**
20915      * Validates the field value
20916      * @return {Boolean} True if the value is valid, else false
20917      */
20918     validate : function(){
20919         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20920             this.clearInvalid();
20921             return true;
20922         }
20923         return false;
20924     },
20925
20926     processValue : function(value){
20927         return value;
20928     },
20929
20930     // private
20931     // Subclasses should provide the validation implementation by overriding this
20932     validateValue : function(value){
20933         return true;
20934     },
20935
20936     /**
20937      * Mark this field as invalid
20938      * @param {String} msg The validation message
20939      */
20940     markInvalid : function(msg){
20941         if(!this.rendered || this.preventMark){ // not rendered
20942             return;
20943         }
20944         
20945         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20946         
20947         obj.el.addClass(this.invalidClass);
20948         msg = msg || this.invalidText;
20949         switch(this.msgTarget){
20950             case 'qtip':
20951                 obj.el.dom.qtip = msg;
20952                 obj.el.dom.qclass = 'x-form-invalid-tip';
20953                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20954                     Roo.QuickTips.enable();
20955                 }
20956                 break;
20957             case 'title':
20958                 this.el.dom.title = msg;
20959                 break;
20960             case 'under':
20961                 if(!this.errorEl){
20962                     var elp = this.el.findParent('.x-form-element', 5, true);
20963                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20964                     this.errorEl.setWidth(elp.getWidth(true)-20);
20965                 }
20966                 this.errorEl.update(msg);
20967                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20968                 break;
20969             case 'side':
20970                 if(!this.errorIcon){
20971                     var elp = this.el.findParent('.x-form-element', 5, true);
20972                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20973                 }
20974                 this.alignErrorIcon();
20975                 this.errorIcon.dom.qtip = msg;
20976                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20977                 this.errorIcon.show();
20978                 this.on('resize', this.alignErrorIcon, this);
20979                 break;
20980             default:
20981                 var t = Roo.getDom(this.msgTarget);
20982                 t.innerHTML = msg;
20983                 t.style.display = this.msgDisplay;
20984                 break;
20985         }
20986         this.fireEvent('invalid', this, msg);
20987     },
20988
20989     // private
20990     alignErrorIcon : function(){
20991         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20992     },
20993
20994     /**
20995      * Clear any invalid styles/messages for this field
20996      */
20997     clearInvalid : function(){
20998         if(!this.rendered || this.preventMark){ // not rendered
20999             return;
21000         }
21001         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21002         
21003         obj.el.removeClass(this.invalidClass);
21004         switch(this.msgTarget){
21005             case 'qtip':
21006                 obj.el.dom.qtip = '';
21007                 break;
21008             case 'title':
21009                 this.el.dom.title = '';
21010                 break;
21011             case 'under':
21012                 if(this.errorEl){
21013                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21014                 }
21015                 break;
21016             case 'side':
21017                 if(this.errorIcon){
21018                     this.errorIcon.dom.qtip = '';
21019                     this.errorIcon.hide();
21020                     this.un('resize', this.alignErrorIcon, this);
21021                 }
21022                 break;
21023             default:
21024                 var t = Roo.getDom(this.msgTarget);
21025                 t.innerHTML = '';
21026                 t.style.display = 'none';
21027                 break;
21028         }
21029         this.fireEvent('valid', this);
21030     },
21031
21032     /**
21033      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21034      * @return {Mixed} value The field value
21035      */
21036     getRawValue : function(){
21037         var v = this.el.getValue();
21038         
21039         return v;
21040     },
21041
21042     /**
21043      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21044      * @return {Mixed} value The field value
21045      */
21046     getValue : function(){
21047         var v = this.el.getValue();
21048          
21049         return v;
21050     },
21051
21052     /**
21053      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21054      * @param {Mixed} value The value to set
21055      */
21056     setRawValue : function(v){
21057         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21058     },
21059
21060     /**
21061      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21062      * @param {Mixed} value The value to set
21063      */
21064     setValue : function(v){
21065         this.value = v;
21066         if(this.rendered){
21067             this.el.dom.value = (v === null || v === undefined ? '' : v);
21068              this.validate();
21069         }
21070     },
21071
21072     adjustSize : function(w, h){
21073         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21074         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21075         return s;
21076     },
21077
21078     adjustWidth : function(tag, w){
21079         tag = tag.toLowerCase();
21080         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21081             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21082                 if(tag == 'input'){
21083                     return w + 2;
21084                 }
21085                 if(tag == 'textarea'){
21086                     return w-2;
21087                 }
21088             }else if(Roo.isOpera){
21089                 if(tag == 'input'){
21090                     return w + 2;
21091                 }
21092                 if(tag == 'textarea'){
21093                     return w-2;
21094                 }
21095             }
21096         }
21097         return w;
21098     }
21099 });
21100
21101
21102 // anything other than normal should be considered experimental
21103 Roo.form.Field.msgFx = {
21104     normal : {
21105         show: function(msgEl, f){
21106             msgEl.setDisplayed('block');
21107         },
21108
21109         hide : function(msgEl, f){
21110             msgEl.setDisplayed(false).update('');
21111         }
21112     },
21113
21114     slide : {
21115         show: function(msgEl, f){
21116             msgEl.slideIn('t', {stopFx:true});
21117         },
21118
21119         hide : function(msgEl, f){
21120             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21121         }
21122     },
21123
21124     slideRight : {
21125         show: function(msgEl, f){
21126             msgEl.fixDisplay();
21127             msgEl.alignTo(f.el, 'tl-tr');
21128             msgEl.slideIn('l', {stopFx:true});
21129         },
21130
21131         hide : function(msgEl, f){
21132             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21133         }
21134     }
21135 };/*
21136  * Based on:
21137  * Ext JS Library 1.1.1
21138  * Copyright(c) 2006-2007, Ext JS, LLC.
21139  *
21140  * Originally Released Under LGPL - original licence link has changed is not relivant.
21141  *
21142  * Fork - LGPL
21143  * <script type="text/javascript">
21144  */
21145  
21146
21147 /**
21148  * @class Roo.form.TextField
21149  * @extends Roo.form.Field
21150  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21151  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21152  * @constructor
21153  * Creates a new TextField
21154  * @param {Object} config Configuration options
21155  */
21156 Roo.form.TextField = function(config){
21157     Roo.form.TextField.superclass.constructor.call(this, config);
21158     this.addEvents({
21159         /**
21160          * @event autosize
21161          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21162          * according to the default logic, but this event provides a hook for the developer to apply additional
21163          * logic at runtime to resize the field if needed.
21164              * @param {Roo.form.Field} this This text field
21165              * @param {Number} width The new field width
21166              */
21167         autosize : true
21168     });
21169 };
21170
21171 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21172     /**
21173      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21174      */
21175     grow : false,
21176     /**
21177      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21178      */
21179     growMin : 30,
21180     /**
21181      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21182      */
21183     growMax : 800,
21184     /**
21185      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21186      */
21187     vtype : null,
21188     /**
21189      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21190      */
21191     maskRe : null,
21192     /**
21193      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21194      */
21195     disableKeyFilter : false,
21196     /**
21197      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21198      */
21199     allowBlank : true,
21200     /**
21201      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21202      */
21203     minLength : 0,
21204     /**
21205      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21206      */
21207     maxLength : Number.MAX_VALUE,
21208     /**
21209      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21210      */
21211     minLengthText : "The minimum length for this field is {0}",
21212     /**
21213      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21214      */
21215     maxLengthText : "The maximum length for this field is {0}",
21216     /**
21217      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21218      */
21219     selectOnFocus : false,
21220     /**
21221      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21222      */
21223     blankText : "This field is required",
21224     /**
21225      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21226      * If available, this function will be called only after the basic validators all return true, and will be passed the
21227      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21228      */
21229     validator : null,
21230     /**
21231      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21232      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21233      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21234      */
21235     regex : null,
21236     /**
21237      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21238      */
21239     regexText : "",
21240     /**
21241      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21242      */
21243     emptyText : null,
21244    
21245
21246     // private
21247     initEvents : function()
21248     {
21249         if (this.emptyText) {
21250             this.el.attr('placeholder', this.emptyText);
21251         }
21252         
21253         Roo.form.TextField.superclass.initEvents.call(this);
21254         if(this.validationEvent == 'keyup'){
21255             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21256             this.el.on('keyup', this.filterValidation, this);
21257         }
21258         else if(this.validationEvent !== false){
21259             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21260         }
21261         
21262         if(this.selectOnFocus){
21263             this.on("focus", this.preFocus, this);
21264             
21265         }
21266         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21267             this.el.on("keypress", this.filterKeys, this);
21268         }
21269         if(this.grow){
21270             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21271             this.el.on("click", this.autoSize,  this);
21272         }
21273         if(this.el.is('input[type=password]') && Roo.isSafari){
21274             this.el.on('keydown', this.SafariOnKeyDown, this);
21275         }
21276     },
21277
21278     processValue : function(value){
21279         if(this.stripCharsRe){
21280             var newValue = value.replace(this.stripCharsRe, '');
21281             if(newValue !== value){
21282                 this.setRawValue(newValue);
21283                 return newValue;
21284             }
21285         }
21286         return value;
21287     },
21288
21289     filterValidation : function(e){
21290         if(!e.isNavKeyPress()){
21291             this.validationTask.delay(this.validationDelay);
21292         }
21293     },
21294
21295     // private
21296     onKeyUp : function(e){
21297         if(!e.isNavKeyPress()){
21298             this.autoSize();
21299         }
21300     },
21301
21302     /**
21303      * Resets the current field value to the originally-loaded value and clears any validation messages.
21304      *  
21305      */
21306     reset : function(){
21307         Roo.form.TextField.superclass.reset.call(this);
21308        
21309     },
21310
21311     
21312     // private
21313     preFocus : function(){
21314         
21315         if(this.selectOnFocus){
21316             this.el.dom.select();
21317         }
21318     },
21319
21320     
21321     // private
21322     filterKeys : function(e){
21323         var k = e.getKey();
21324         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21325             return;
21326         }
21327         var c = e.getCharCode(), cc = String.fromCharCode(c);
21328         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21329             return;
21330         }
21331         if(!this.maskRe.test(cc)){
21332             e.stopEvent();
21333         }
21334     },
21335
21336     setValue : function(v){
21337         
21338         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21339         
21340         this.autoSize();
21341     },
21342
21343     /**
21344      * Validates a value according to the field's validation rules and marks the field as invalid
21345      * if the validation fails
21346      * @param {Mixed} value The value to validate
21347      * @return {Boolean} True if the value is valid, else false
21348      */
21349     validateValue : function(value){
21350         if(value.length < 1)  { // if it's blank
21351              if(this.allowBlank){
21352                 this.clearInvalid();
21353                 return true;
21354              }else{
21355                 this.markInvalid(this.blankText);
21356                 return false;
21357              }
21358         }
21359         if(value.length < this.minLength){
21360             this.markInvalid(String.format(this.minLengthText, this.minLength));
21361             return false;
21362         }
21363         if(value.length > this.maxLength){
21364             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21365             return false;
21366         }
21367         if(this.vtype){
21368             var vt = Roo.form.VTypes;
21369             if(!vt[this.vtype](value, this)){
21370                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21371                 return false;
21372             }
21373         }
21374         if(typeof this.validator == "function"){
21375             var msg = this.validator(value);
21376             if(msg !== true){
21377                 this.markInvalid(msg);
21378                 return false;
21379             }
21380         }
21381         if(this.regex && !this.regex.test(value)){
21382             this.markInvalid(this.regexText);
21383             return false;
21384         }
21385         return true;
21386     },
21387
21388     /**
21389      * Selects text in this field
21390      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21391      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21392      */
21393     selectText : function(start, end){
21394         var v = this.getRawValue();
21395         if(v.length > 0){
21396             start = start === undefined ? 0 : start;
21397             end = end === undefined ? v.length : end;
21398             var d = this.el.dom;
21399             if(d.setSelectionRange){
21400                 d.setSelectionRange(start, end);
21401             }else if(d.createTextRange){
21402                 var range = d.createTextRange();
21403                 range.moveStart("character", start);
21404                 range.moveEnd("character", v.length-end);
21405                 range.select();
21406             }
21407         }
21408     },
21409
21410     /**
21411      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21412      * This only takes effect if grow = true, and fires the autosize event.
21413      */
21414     autoSize : function(){
21415         if(!this.grow || !this.rendered){
21416             return;
21417         }
21418         if(!this.metrics){
21419             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21420         }
21421         var el = this.el;
21422         var v = el.dom.value;
21423         var d = document.createElement('div');
21424         d.appendChild(document.createTextNode(v));
21425         v = d.innerHTML;
21426         d = null;
21427         v += "&#160;";
21428         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21429         this.el.setWidth(w);
21430         this.fireEvent("autosize", this, w);
21431     },
21432     
21433     // private
21434     SafariOnKeyDown : function(event)
21435     {
21436         // this is a workaround for a password hang bug on chrome/ webkit.
21437         
21438         var isSelectAll = false;
21439         
21440         if(this.el.dom.selectionEnd > 0){
21441             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21442         }
21443         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21444             event.preventDefault();
21445             this.setValue('');
21446             return;
21447         }
21448         
21449         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21450             
21451             event.preventDefault();
21452             // this is very hacky as keydown always get's upper case.
21453             
21454             var cc = String.fromCharCode(event.getCharCode());
21455             
21456             
21457             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21458             
21459         }
21460         
21461         
21462     }
21463 });/*
21464  * Based on:
21465  * Ext JS Library 1.1.1
21466  * Copyright(c) 2006-2007, Ext JS, LLC.
21467  *
21468  * Originally Released Under LGPL - original licence link has changed is not relivant.
21469  *
21470  * Fork - LGPL
21471  * <script type="text/javascript">
21472  */
21473  
21474 /**
21475  * @class Roo.form.Hidden
21476  * @extends Roo.form.TextField
21477  * Simple Hidden element used on forms 
21478  * 
21479  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21480  * 
21481  * @constructor
21482  * Creates a new Hidden form element.
21483  * @param {Object} config Configuration options
21484  */
21485
21486
21487
21488 // easy hidden field...
21489 Roo.form.Hidden = function(config){
21490     Roo.form.Hidden.superclass.constructor.call(this, config);
21491 };
21492   
21493 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21494     fieldLabel:      '',
21495     inputType:      'hidden',
21496     width:          50,
21497     allowBlank:     true,
21498     labelSeparator: '',
21499     hidden:         true,
21500     itemCls :       'x-form-item-display-none'
21501
21502
21503 });
21504
21505
21506 /*
21507  * Based on:
21508  * Ext JS Library 1.1.1
21509  * Copyright(c) 2006-2007, Ext JS, LLC.
21510  *
21511  * Originally Released Under LGPL - original licence link has changed is not relivant.
21512  *
21513  * Fork - LGPL
21514  * <script type="text/javascript">
21515  */
21516  
21517 /**
21518  * @class Roo.form.TriggerField
21519  * @extends Roo.form.TextField
21520  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21521  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21522  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21523  * for which you can provide a custom implementation.  For example:
21524  * <pre><code>
21525 var trigger = new Roo.form.TriggerField();
21526 trigger.onTriggerClick = myTriggerFn;
21527 trigger.applyTo('my-field');
21528 </code></pre>
21529  *
21530  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21531  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21532  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21533  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21534  * @constructor
21535  * Create a new TriggerField.
21536  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21537  * to the base TextField)
21538  */
21539 Roo.form.TriggerField = function(config){
21540     this.mimicing = false;
21541     Roo.form.TriggerField.superclass.constructor.call(this, config);
21542 };
21543
21544 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21545     /**
21546      * @cfg {String} triggerClass A CSS class to apply to the trigger
21547      */
21548     /**
21549      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21550      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21551      */
21552     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21553     /**
21554      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21555      */
21556     hideTrigger:false,
21557
21558     /** @cfg {Boolean} grow @hide */
21559     /** @cfg {Number} growMin @hide */
21560     /** @cfg {Number} growMax @hide */
21561
21562     /**
21563      * @hide 
21564      * @method
21565      */
21566     autoSize: Roo.emptyFn,
21567     // private
21568     monitorTab : true,
21569     // private
21570     deferHeight : true,
21571
21572     
21573     actionMode : 'wrap',
21574     // private
21575     onResize : function(w, h){
21576         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21577         if(typeof w == 'number'){
21578             var x = w - this.trigger.getWidth();
21579             this.el.setWidth(this.adjustWidth('input', x));
21580             this.trigger.setStyle('left', x+'px');
21581         }
21582     },
21583
21584     // private
21585     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21586
21587     // private
21588     getResizeEl : function(){
21589         return this.wrap;
21590     },
21591
21592     // private
21593     getPositionEl : function(){
21594         return this.wrap;
21595     },
21596
21597     // private
21598     alignErrorIcon : function(){
21599         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21600     },
21601
21602     // private
21603     onRender : function(ct, position){
21604         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21605         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21606         this.trigger = this.wrap.createChild(this.triggerConfig ||
21607                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21608         if(this.hideTrigger){
21609             this.trigger.setDisplayed(false);
21610         }
21611         this.initTrigger();
21612         if(!this.width){
21613             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21614         }
21615     },
21616
21617     // private
21618     initTrigger : function(){
21619         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21620         this.trigger.addClassOnOver('x-form-trigger-over');
21621         this.trigger.addClassOnClick('x-form-trigger-click');
21622     },
21623
21624     // private
21625     onDestroy : function(){
21626         if(this.trigger){
21627             this.trigger.removeAllListeners();
21628             this.trigger.remove();
21629         }
21630         if(this.wrap){
21631             this.wrap.remove();
21632         }
21633         Roo.form.TriggerField.superclass.onDestroy.call(this);
21634     },
21635
21636     // private
21637     onFocus : function(){
21638         Roo.form.TriggerField.superclass.onFocus.call(this);
21639         if(!this.mimicing){
21640             this.wrap.addClass('x-trigger-wrap-focus');
21641             this.mimicing = true;
21642             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21643             if(this.monitorTab){
21644                 this.el.on("keydown", this.checkTab, this);
21645             }
21646         }
21647     },
21648
21649     // private
21650     checkTab : function(e){
21651         if(e.getKey() == e.TAB){
21652             this.triggerBlur();
21653         }
21654     },
21655
21656     // private
21657     onBlur : function(){
21658         // do nothing
21659     },
21660
21661     // private
21662     mimicBlur : function(e, t){
21663         if(!this.wrap.contains(t) && this.validateBlur()){
21664             this.triggerBlur();
21665         }
21666     },
21667
21668     // private
21669     triggerBlur : function(){
21670         this.mimicing = false;
21671         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21672         if(this.monitorTab){
21673             this.el.un("keydown", this.checkTab, this);
21674         }
21675         this.wrap.removeClass('x-trigger-wrap-focus');
21676         Roo.form.TriggerField.superclass.onBlur.call(this);
21677     },
21678
21679     // private
21680     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21681     validateBlur : function(e, t){
21682         return true;
21683     },
21684
21685     // private
21686     onDisable : function(){
21687         Roo.form.TriggerField.superclass.onDisable.call(this);
21688         if(this.wrap){
21689             this.wrap.addClass('x-item-disabled');
21690         }
21691     },
21692
21693     // private
21694     onEnable : function(){
21695         Roo.form.TriggerField.superclass.onEnable.call(this);
21696         if(this.wrap){
21697             this.wrap.removeClass('x-item-disabled');
21698         }
21699     },
21700
21701     // private
21702     onShow : function(){
21703         var ae = this.getActionEl();
21704         
21705         if(ae){
21706             ae.dom.style.display = '';
21707             ae.dom.style.visibility = 'visible';
21708         }
21709     },
21710
21711     // private
21712     
21713     onHide : function(){
21714         var ae = this.getActionEl();
21715         ae.dom.style.display = 'none';
21716     },
21717
21718     /**
21719      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21720      * by an implementing function.
21721      * @method
21722      * @param {EventObject} e
21723      */
21724     onTriggerClick : Roo.emptyFn
21725 });
21726
21727 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21728 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21729 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21730 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21731     initComponent : function(){
21732         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21733
21734         this.triggerConfig = {
21735             tag:'span', cls:'x-form-twin-triggers', cn:[
21736             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21737             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21738         ]};
21739     },
21740
21741     getTrigger : function(index){
21742         return this.triggers[index];
21743     },
21744
21745     initTrigger : function(){
21746         var ts = this.trigger.select('.x-form-trigger', true);
21747         this.wrap.setStyle('overflow', 'hidden');
21748         var triggerField = this;
21749         ts.each(function(t, all, index){
21750             t.hide = function(){
21751                 var w = triggerField.wrap.getWidth();
21752                 this.dom.style.display = 'none';
21753                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21754             };
21755             t.show = function(){
21756                 var w = triggerField.wrap.getWidth();
21757                 this.dom.style.display = '';
21758                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21759             };
21760             var triggerIndex = 'Trigger'+(index+1);
21761
21762             if(this['hide'+triggerIndex]){
21763                 t.dom.style.display = 'none';
21764             }
21765             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21766             t.addClassOnOver('x-form-trigger-over');
21767             t.addClassOnClick('x-form-trigger-click');
21768         }, this);
21769         this.triggers = ts.elements;
21770     },
21771
21772     onTrigger1Click : Roo.emptyFn,
21773     onTrigger2Click : Roo.emptyFn
21774 });/*
21775  * Based on:
21776  * Ext JS Library 1.1.1
21777  * Copyright(c) 2006-2007, Ext JS, LLC.
21778  *
21779  * Originally Released Under LGPL - original licence link has changed is not relivant.
21780  *
21781  * Fork - LGPL
21782  * <script type="text/javascript">
21783  */
21784  
21785 /**
21786  * @class Roo.form.TextArea
21787  * @extends Roo.form.TextField
21788  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21789  * support for auto-sizing.
21790  * @constructor
21791  * Creates a new TextArea
21792  * @param {Object} config Configuration options
21793  */
21794 Roo.form.TextArea = function(config){
21795     Roo.form.TextArea.superclass.constructor.call(this, config);
21796     // these are provided exchanges for backwards compat
21797     // minHeight/maxHeight were replaced by growMin/growMax to be
21798     // compatible with TextField growing config values
21799     if(this.minHeight !== undefined){
21800         this.growMin = this.minHeight;
21801     }
21802     if(this.maxHeight !== undefined){
21803         this.growMax = this.maxHeight;
21804     }
21805 };
21806
21807 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21808     /**
21809      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21810      */
21811     growMin : 60,
21812     /**
21813      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21814      */
21815     growMax: 1000,
21816     /**
21817      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21818      * in the field (equivalent to setting overflow: hidden, defaults to false)
21819      */
21820     preventScrollbars: false,
21821     /**
21822      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21823      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21824      */
21825
21826     // private
21827     onRender : function(ct, position){
21828         if(!this.el){
21829             this.defaultAutoCreate = {
21830                 tag: "textarea",
21831                 style:"width:300px;height:60px;",
21832                 autocomplete: "new-password"
21833             };
21834         }
21835         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21836         if(this.grow){
21837             this.textSizeEl = Roo.DomHelper.append(document.body, {
21838                 tag: "pre", cls: "x-form-grow-sizer"
21839             });
21840             if(this.preventScrollbars){
21841                 this.el.setStyle("overflow", "hidden");
21842             }
21843             this.el.setHeight(this.growMin);
21844         }
21845     },
21846
21847     onDestroy : function(){
21848         if(this.textSizeEl){
21849             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21850         }
21851         Roo.form.TextArea.superclass.onDestroy.call(this);
21852     },
21853
21854     // private
21855     onKeyUp : function(e){
21856         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21857             this.autoSize();
21858         }
21859     },
21860
21861     /**
21862      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21863      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21864      */
21865     autoSize : function(){
21866         if(!this.grow || !this.textSizeEl){
21867             return;
21868         }
21869         var el = this.el;
21870         var v = el.dom.value;
21871         var ts = this.textSizeEl;
21872
21873         ts.innerHTML = '';
21874         ts.appendChild(document.createTextNode(v));
21875         v = ts.innerHTML;
21876
21877         Roo.fly(ts).setWidth(this.el.getWidth());
21878         if(v.length < 1){
21879             v = "&#160;&#160;";
21880         }else{
21881             if(Roo.isIE){
21882                 v = v.replace(/\n/g, '<p>&#160;</p>');
21883             }
21884             v += "&#160;\n&#160;";
21885         }
21886         ts.innerHTML = v;
21887         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21888         if(h != this.lastHeight){
21889             this.lastHeight = h;
21890             this.el.setHeight(h);
21891             this.fireEvent("autosize", this, h);
21892         }
21893     }
21894 });/*
21895  * Based on:
21896  * Ext JS Library 1.1.1
21897  * Copyright(c) 2006-2007, Ext JS, LLC.
21898  *
21899  * Originally Released Under LGPL - original licence link has changed is not relivant.
21900  *
21901  * Fork - LGPL
21902  * <script type="text/javascript">
21903  */
21904  
21905
21906 /**
21907  * @class Roo.form.NumberField
21908  * @extends Roo.form.TextField
21909  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21910  * @constructor
21911  * Creates a new NumberField
21912  * @param {Object} config Configuration options
21913  */
21914 Roo.form.NumberField = function(config){
21915     Roo.form.NumberField.superclass.constructor.call(this, config);
21916 };
21917
21918 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21919     /**
21920      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21921      */
21922     fieldClass: "x-form-field x-form-num-field",
21923     /**
21924      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21925      */
21926     allowDecimals : true,
21927     /**
21928      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21929      */
21930     decimalSeparator : ".",
21931     /**
21932      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21933      */
21934     decimalPrecision : 2,
21935     /**
21936      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21937      */
21938     allowNegative : true,
21939     /**
21940      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21941      */
21942     minValue : Number.NEGATIVE_INFINITY,
21943     /**
21944      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21945      */
21946     maxValue : Number.MAX_VALUE,
21947     /**
21948      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21949      */
21950     minText : "The minimum value for this field is {0}",
21951     /**
21952      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21953      */
21954     maxText : "The maximum value for this field is {0}",
21955     /**
21956      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21957      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21958      */
21959     nanText : "{0} is not a valid number",
21960
21961     // private
21962     initEvents : function(){
21963         Roo.form.NumberField.superclass.initEvents.call(this);
21964         var allowed = "0123456789";
21965         if(this.allowDecimals){
21966             allowed += this.decimalSeparator;
21967         }
21968         if(this.allowNegative){
21969             allowed += "-";
21970         }
21971         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21972         var keyPress = function(e){
21973             var k = e.getKey();
21974             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21975                 return;
21976             }
21977             var c = e.getCharCode();
21978             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21979                 e.stopEvent();
21980             }
21981         };
21982         this.el.on("keypress", keyPress, this);
21983     },
21984
21985     // private
21986     validateValue : function(value){
21987         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21988             return false;
21989         }
21990         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21991              return true;
21992         }
21993         var num = this.parseValue(value);
21994         if(isNaN(num)){
21995             this.markInvalid(String.format(this.nanText, value));
21996             return false;
21997         }
21998         if(num < this.minValue){
21999             this.markInvalid(String.format(this.minText, this.minValue));
22000             return false;
22001         }
22002         if(num > this.maxValue){
22003             this.markInvalid(String.format(this.maxText, this.maxValue));
22004             return false;
22005         }
22006         return true;
22007     },
22008
22009     getValue : function(){
22010         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22011     },
22012
22013     // private
22014     parseValue : function(value){
22015         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22016         return isNaN(value) ? '' : value;
22017     },
22018
22019     // private
22020     fixPrecision : function(value){
22021         var nan = isNaN(value);
22022         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22023             return nan ? '' : value;
22024         }
22025         return parseFloat(value).toFixed(this.decimalPrecision);
22026     },
22027
22028     setValue : function(v){
22029         v = this.fixPrecision(v);
22030         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22031     },
22032
22033     // private
22034     decimalPrecisionFcn : function(v){
22035         return Math.floor(v);
22036     },
22037
22038     beforeBlur : function(){
22039         var v = this.parseValue(this.getRawValue());
22040         if(v){
22041             this.setValue(v);
22042         }
22043     }
22044 });/*
22045  * Based on:
22046  * Ext JS Library 1.1.1
22047  * Copyright(c) 2006-2007, Ext JS, LLC.
22048  *
22049  * Originally Released Under LGPL - original licence link has changed is not relivant.
22050  *
22051  * Fork - LGPL
22052  * <script type="text/javascript">
22053  */
22054  
22055 /**
22056  * @class Roo.form.DateField
22057  * @extends Roo.form.TriggerField
22058  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22059 * @constructor
22060 * Create a new DateField
22061 * @param {Object} config
22062  */
22063 Roo.form.DateField = function(config){
22064     Roo.form.DateField.superclass.constructor.call(this, config);
22065     
22066       this.addEvents({
22067          
22068         /**
22069          * @event select
22070          * Fires when a date is selected
22071              * @param {Roo.form.DateField} combo This combo box
22072              * @param {Date} date The date selected
22073              */
22074         'select' : true
22075          
22076     });
22077     
22078     
22079     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22080     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22081     this.ddMatch = null;
22082     if(this.disabledDates){
22083         var dd = this.disabledDates;
22084         var re = "(?:";
22085         for(var i = 0; i < dd.length; i++){
22086             re += dd[i];
22087             if(i != dd.length-1) re += "|";
22088         }
22089         this.ddMatch = new RegExp(re + ")");
22090     }
22091 };
22092
22093 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22094     /**
22095      * @cfg {String} format
22096      * The default date format string which can be overriden for localization support.  The format must be
22097      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22098      */
22099     format : "m/d/y",
22100     /**
22101      * @cfg {String} altFormats
22102      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22103      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22104      */
22105     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22106     /**
22107      * @cfg {Array} disabledDays
22108      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22109      */
22110     disabledDays : null,
22111     /**
22112      * @cfg {String} disabledDaysText
22113      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22114      */
22115     disabledDaysText : "Disabled",
22116     /**
22117      * @cfg {Array} disabledDates
22118      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22119      * expression so they are very powerful. Some examples:
22120      * <ul>
22121      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22122      * <li>["03/08", "09/16"] would disable those days for every year</li>
22123      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22124      * <li>["03/../2006"] would disable every day in March 2006</li>
22125      * <li>["^03"] would disable every day in every March</li>
22126      * </ul>
22127      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22128      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22129      */
22130     disabledDates : null,
22131     /**
22132      * @cfg {String} disabledDatesText
22133      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22134      */
22135     disabledDatesText : "Disabled",
22136     /**
22137      * @cfg {Date/String} minValue
22138      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22139      * valid format (defaults to null).
22140      */
22141     minValue : null,
22142     /**
22143      * @cfg {Date/String} maxValue
22144      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22145      * valid format (defaults to null).
22146      */
22147     maxValue : null,
22148     /**
22149      * @cfg {String} minText
22150      * The error text to display when the date in the cell is before minValue (defaults to
22151      * 'The date in this field must be after {minValue}').
22152      */
22153     minText : "The date in this field must be equal to or after {0}",
22154     /**
22155      * @cfg {String} maxText
22156      * The error text to display when the date in the cell is after maxValue (defaults to
22157      * 'The date in this field must be before {maxValue}').
22158      */
22159     maxText : "The date in this field must be equal to or before {0}",
22160     /**
22161      * @cfg {String} invalidText
22162      * The error text to display when the date in the field is invalid (defaults to
22163      * '{value} is not a valid date - it must be in the format {format}').
22164      */
22165     invalidText : "{0} is not a valid date - it must be in the format {1}",
22166     /**
22167      * @cfg {String} triggerClass
22168      * An additional CSS class used to style the trigger button.  The trigger will always get the
22169      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22170      * which displays a calendar icon).
22171      */
22172     triggerClass : 'x-form-date-trigger',
22173     
22174
22175     /**
22176      * @cfg {Boolean} useIso
22177      * if enabled, then the date field will use a hidden field to store the 
22178      * real value as iso formated date. default (false)
22179      */ 
22180     useIso : false,
22181     /**
22182      * @cfg {String/Object} autoCreate
22183      * A DomHelper element spec, or true for a default element spec (defaults to
22184      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22185      */ 
22186     // private
22187     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22188     
22189     // private
22190     hiddenField: false,
22191     
22192     onRender : function(ct, position)
22193     {
22194         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22195         if (this.useIso) {
22196             //this.el.dom.removeAttribute('name'); 
22197             Roo.log("Changing name?");
22198             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22199             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22200                     'before', true);
22201             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22202             // prevent input submission
22203             this.hiddenName = this.name;
22204         }
22205             
22206             
22207     },
22208     
22209     // private
22210     validateValue : function(value)
22211     {
22212         value = this.formatDate(value);
22213         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22214             Roo.log('super failed');
22215             return false;
22216         }
22217         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22218              return true;
22219         }
22220         var svalue = value;
22221         value = this.parseDate(value);
22222         if(!value){
22223             Roo.log('parse date failed' + svalue);
22224             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22225             return false;
22226         }
22227         var time = value.getTime();
22228         if(this.minValue && time < this.minValue.getTime()){
22229             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22230             return false;
22231         }
22232         if(this.maxValue && time > this.maxValue.getTime()){
22233             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22234             return false;
22235         }
22236         if(this.disabledDays){
22237             var day = value.getDay();
22238             for(var i = 0; i < this.disabledDays.length; i++) {
22239                 if(day === this.disabledDays[i]){
22240                     this.markInvalid(this.disabledDaysText);
22241                     return false;
22242                 }
22243             }
22244         }
22245         var fvalue = this.formatDate(value);
22246         if(this.ddMatch && this.ddMatch.test(fvalue)){
22247             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22248             return false;
22249         }
22250         return true;
22251     },
22252
22253     // private
22254     // Provides logic to override the default TriggerField.validateBlur which just returns true
22255     validateBlur : function(){
22256         return !this.menu || !this.menu.isVisible();
22257     },
22258     
22259     getName: function()
22260     {
22261         // returns hidden if it's set..
22262         if (!this.rendered) {return ''};
22263         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22264         
22265     },
22266
22267     /**
22268      * Returns the current date value of the date field.
22269      * @return {Date} The date value
22270      */
22271     getValue : function(){
22272         
22273         return  this.hiddenField ?
22274                 this.hiddenField.value :
22275                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22276     },
22277
22278     /**
22279      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22280      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22281      * (the default format used is "m/d/y").
22282      * <br />Usage:
22283      * <pre><code>
22284 //All of these calls set the same date value (May 4, 2006)
22285
22286 //Pass a date object:
22287 var dt = new Date('5/4/06');
22288 dateField.setValue(dt);
22289
22290 //Pass a date string (default format):
22291 dateField.setValue('5/4/06');
22292
22293 //Pass a date string (custom format):
22294 dateField.format = 'Y-m-d';
22295 dateField.setValue('2006-5-4');
22296 </code></pre>
22297      * @param {String/Date} date The date or valid date string
22298      */
22299     setValue : function(date){
22300         if (this.hiddenField) {
22301             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22302         }
22303         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22304         // make sure the value field is always stored as a date..
22305         this.value = this.parseDate(date);
22306         
22307         
22308     },
22309
22310     // private
22311     parseDate : function(value){
22312         if(!value || value instanceof Date){
22313             return value;
22314         }
22315         var v = Date.parseDate(value, this.format);
22316          if (!v && this.useIso) {
22317             v = Date.parseDate(value, 'Y-m-d');
22318         }
22319         if(!v && this.altFormats){
22320             if(!this.altFormatsArray){
22321                 this.altFormatsArray = this.altFormats.split("|");
22322             }
22323             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22324                 v = Date.parseDate(value, this.altFormatsArray[i]);
22325             }
22326         }
22327         return v;
22328     },
22329
22330     // private
22331     formatDate : function(date, fmt){
22332         return (!date || !(date instanceof Date)) ?
22333                date : date.dateFormat(fmt || this.format);
22334     },
22335
22336     // private
22337     menuListeners : {
22338         select: function(m, d){
22339             
22340             this.setValue(d);
22341             this.fireEvent('select', this, d);
22342         },
22343         show : function(){ // retain focus styling
22344             this.onFocus();
22345         },
22346         hide : function(){
22347             this.focus.defer(10, this);
22348             var ml = this.menuListeners;
22349             this.menu.un("select", ml.select,  this);
22350             this.menu.un("show", ml.show,  this);
22351             this.menu.un("hide", ml.hide,  this);
22352         }
22353     },
22354
22355     // private
22356     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22357     onTriggerClick : function(){
22358         if(this.disabled){
22359             return;
22360         }
22361         if(this.menu == null){
22362             this.menu = new Roo.menu.DateMenu();
22363         }
22364         Roo.apply(this.menu.picker,  {
22365             showClear: this.allowBlank,
22366             minDate : this.minValue,
22367             maxDate : this.maxValue,
22368             disabledDatesRE : this.ddMatch,
22369             disabledDatesText : this.disabledDatesText,
22370             disabledDays : this.disabledDays,
22371             disabledDaysText : this.disabledDaysText,
22372             format : this.useIso ? 'Y-m-d' : this.format,
22373             minText : String.format(this.minText, this.formatDate(this.minValue)),
22374             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22375         });
22376         this.menu.on(Roo.apply({}, this.menuListeners, {
22377             scope:this
22378         }));
22379         this.menu.picker.setValue(this.getValue() || new Date());
22380         this.menu.show(this.el, "tl-bl?");
22381     },
22382
22383     beforeBlur : function(){
22384         var v = this.parseDate(this.getRawValue());
22385         if(v){
22386             this.setValue(v);
22387         }
22388     },
22389
22390     /*@
22391      * overide
22392      * 
22393      */
22394     isDirty : function() {
22395         if(this.disabled) {
22396             return false;
22397         }
22398         
22399         if(typeof(this.startValue) === 'undefined'){
22400             return false;
22401         }
22402         
22403         return String(this.getValue()) !== String(this.startValue);
22404         
22405     }
22406 });/*
22407  * Based on:
22408  * Ext JS Library 1.1.1
22409  * Copyright(c) 2006-2007, Ext JS, LLC.
22410  *
22411  * Originally Released Under LGPL - original licence link has changed is not relivant.
22412  *
22413  * Fork - LGPL
22414  * <script type="text/javascript">
22415  */
22416  
22417 /**
22418  * @class Roo.form.MonthField
22419  * @extends Roo.form.TriggerField
22420  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22421 * @constructor
22422 * Create a new MonthField
22423 * @param {Object} config
22424  */
22425 Roo.form.MonthField = function(config){
22426     
22427     Roo.form.MonthField.superclass.constructor.call(this, config);
22428     
22429       this.addEvents({
22430          
22431         /**
22432          * @event select
22433          * Fires when a date is selected
22434              * @param {Roo.form.MonthFieeld} combo This combo box
22435              * @param {Date} date The date selected
22436              */
22437         'select' : true
22438          
22439     });
22440     
22441     
22442     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22443     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22444     this.ddMatch = null;
22445     if(this.disabledDates){
22446         var dd = this.disabledDates;
22447         var re = "(?:";
22448         for(var i = 0; i < dd.length; i++){
22449             re += dd[i];
22450             if(i != dd.length-1) re += "|";
22451         }
22452         this.ddMatch = new RegExp(re + ")");
22453     }
22454 };
22455
22456 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22457     /**
22458      * @cfg {String} format
22459      * The default date format string which can be overriden for localization support.  The format must be
22460      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22461      */
22462     format : "M Y",
22463     /**
22464      * @cfg {String} altFormats
22465      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22466      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22467      */
22468     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22469     /**
22470      * @cfg {Array} disabledDays
22471      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22472      */
22473     disabledDays : [0,1,2,3,4,5,6],
22474     /**
22475      * @cfg {String} disabledDaysText
22476      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22477      */
22478     disabledDaysText : "Disabled",
22479     /**
22480      * @cfg {Array} disabledDates
22481      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22482      * expression so they are very powerful. Some examples:
22483      * <ul>
22484      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22485      * <li>["03/08", "09/16"] would disable those days for every year</li>
22486      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22487      * <li>["03/../2006"] would disable every day in March 2006</li>
22488      * <li>["^03"] would disable every day in every March</li>
22489      * </ul>
22490      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22491      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22492      */
22493     disabledDates : null,
22494     /**
22495      * @cfg {String} disabledDatesText
22496      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22497      */
22498     disabledDatesText : "Disabled",
22499     /**
22500      * @cfg {Date/String} minValue
22501      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22502      * valid format (defaults to null).
22503      */
22504     minValue : null,
22505     /**
22506      * @cfg {Date/String} maxValue
22507      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22508      * valid format (defaults to null).
22509      */
22510     maxValue : null,
22511     /**
22512      * @cfg {String} minText
22513      * The error text to display when the date in the cell is before minValue (defaults to
22514      * 'The date in this field must be after {minValue}').
22515      */
22516     minText : "The date in this field must be equal to or after {0}",
22517     /**
22518      * @cfg {String} maxTextf
22519      * The error text to display when the date in the cell is after maxValue (defaults to
22520      * 'The date in this field must be before {maxValue}').
22521      */
22522     maxText : "The date in this field must be equal to or before {0}",
22523     /**
22524      * @cfg {String} invalidText
22525      * The error text to display when the date in the field is invalid (defaults to
22526      * '{value} is not a valid date - it must be in the format {format}').
22527      */
22528     invalidText : "{0} is not a valid date - it must be in the format {1}",
22529     /**
22530      * @cfg {String} triggerClass
22531      * An additional CSS class used to style the trigger button.  The trigger will always get the
22532      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22533      * which displays a calendar icon).
22534      */
22535     triggerClass : 'x-form-date-trigger',
22536     
22537
22538     /**
22539      * @cfg {Boolean} useIso
22540      * if enabled, then the date field will use a hidden field to store the 
22541      * real value as iso formated date. default (true)
22542      */ 
22543     useIso : true,
22544     /**
22545      * @cfg {String/Object} autoCreate
22546      * A DomHelper element spec, or true for a default element spec (defaults to
22547      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22548      */ 
22549     // private
22550     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22551     
22552     // private
22553     hiddenField: false,
22554     
22555     hideMonthPicker : false,
22556     
22557     onRender : function(ct, position)
22558     {
22559         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22560         if (this.useIso) {
22561             this.el.dom.removeAttribute('name'); 
22562             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22563                     'before', true);
22564             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22565             // prevent input submission
22566             this.hiddenName = this.name;
22567         }
22568             
22569             
22570     },
22571     
22572     // private
22573     validateValue : function(value)
22574     {
22575         value = this.formatDate(value);
22576         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22577             return false;
22578         }
22579         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22580              return true;
22581         }
22582         var svalue = value;
22583         value = this.parseDate(value);
22584         if(!value){
22585             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22586             return false;
22587         }
22588         var time = value.getTime();
22589         if(this.minValue && time < this.minValue.getTime()){
22590             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22591             return false;
22592         }
22593         if(this.maxValue && time > this.maxValue.getTime()){
22594             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22595             return false;
22596         }
22597         /*if(this.disabledDays){
22598             var day = value.getDay();
22599             for(var i = 0; i < this.disabledDays.length; i++) {
22600                 if(day === this.disabledDays[i]){
22601                     this.markInvalid(this.disabledDaysText);
22602                     return false;
22603                 }
22604             }
22605         }
22606         */
22607         var fvalue = this.formatDate(value);
22608         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22609             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22610             return false;
22611         }
22612         */
22613         return true;
22614     },
22615
22616     // private
22617     // Provides logic to override the default TriggerField.validateBlur which just returns true
22618     validateBlur : function(){
22619         return !this.menu || !this.menu.isVisible();
22620     },
22621
22622     /**
22623      * Returns the current date value of the date field.
22624      * @return {Date} The date value
22625      */
22626     getValue : function(){
22627         
22628         
22629         
22630         return  this.hiddenField ?
22631                 this.hiddenField.value :
22632                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22633     },
22634
22635     /**
22636      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22637      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22638      * (the default format used is "m/d/y").
22639      * <br />Usage:
22640      * <pre><code>
22641 //All of these calls set the same date value (May 4, 2006)
22642
22643 //Pass a date object:
22644 var dt = new Date('5/4/06');
22645 monthField.setValue(dt);
22646
22647 //Pass a date string (default format):
22648 monthField.setValue('5/4/06');
22649
22650 //Pass a date string (custom format):
22651 monthField.format = 'Y-m-d';
22652 monthField.setValue('2006-5-4');
22653 </code></pre>
22654      * @param {String/Date} date The date or valid date string
22655      */
22656     setValue : function(date){
22657         Roo.log('month setValue' + date);
22658         // can only be first of month..
22659         
22660         var val = this.parseDate(date);
22661         
22662         if (this.hiddenField) {
22663             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22664         }
22665         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22666         this.value = this.parseDate(date);
22667     },
22668
22669     // private
22670     parseDate : function(value){
22671         if(!value || value instanceof Date){
22672             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22673             return value;
22674         }
22675         var v = Date.parseDate(value, this.format);
22676         if (!v && this.useIso) {
22677             v = Date.parseDate(value, 'Y-m-d');
22678         }
22679         if (v) {
22680             // 
22681             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22682         }
22683         
22684         
22685         if(!v && this.altFormats){
22686             if(!this.altFormatsArray){
22687                 this.altFormatsArray = this.altFormats.split("|");
22688             }
22689             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22690                 v = Date.parseDate(value, this.altFormatsArray[i]);
22691             }
22692         }
22693         return v;
22694     },
22695
22696     // private
22697     formatDate : function(date, fmt){
22698         return (!date || !(date instanceof Date)) ?
22699                date : date.dateFormat(fmt || this.format);
22700     },
22701
22702     // private
22703     menuListeners : {
22704         select: function(m, d){
22705             this.setValue(d);
22706             this.fireEvent('select', this, d);
22707         },
22708         show : function(){ // retain focus styling
22709             this.onFocus();
22710         },
22711         hide : function(){
22712             this.focus.defer(10, this);
22713             var ml = this.menuListeners;
22714             this.menu.un("select", ml.select,  this);
22715             this.menu.un("show", ml.show,  this);
22716             this.menu.un("hide", ml.hide,  this);
22717         }
22718     },
22719     // private
22720     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22721     onTriggerClick : function(){
22722         if(this.disabled){
22723             return;
22724         }
22725         if(this.menu == null){
22726             this.menu = new Roo.menu.DateMenu();
22727            
22728         }
22729         
22730         Roo.apply(this.menu.picker,  {
22731             
22732             showClear: this.allowBlank,
22733             minDate : this.minValue,
22734             maxDate : this.maxValue,
22735             disabledDatesRE : this.ddMatch,
22736             disabledDatesText : this.disabledDatesText,
22737             
22738             format : this.useIso ? 'Y-m-d' : this.format,
22739             minText : String.format(this.minText, this.formatDate(this.minValue)),
22740             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22741             
22742         });
22743          this.menu.on(Roo.apply({}, this.menuListeners, {
22744             scope:this
22745         }));
22746        
22747         
22748         var m = this.menu;
22749         var p = m.picker;
22750         
22751         // hide month picker get's called when we called by 'before hide';
22752         
22753         var ignorehide = true;
22754         p.hideMonthPicker  = function(disableAnim){
22755             if (ignorehide) {
22756                 return;
22757             }
22758              if(this.monthPicker){
22759                 Roo.log("hideMonthPicker called");
22760                 if(disableAnim === true){
22761                     this.monthPicker.hide();
22762                 }else{
22763                     this.monthPicker.slideOut('t', {duration:.2});
22764                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22765                     p.fireEvent("select", this, this.value);
22766                     m.hide();
22767                 }
22768             }
22769         }
22770         
22771         Roo.log('picker set value');
22772         Roo.log(this.getValue());
22773         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22774         m.show(this.el, 'tl-bl?');
22775         ignorehide  = false;
22776         // this will trigger hideMonthPicker..
22777         
22778         
22779         // hidden the day picker
22780         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22781         
22782         
22783         
22784       
22785         
22786         p.showMonthPicker.defer(100, p);
22787     
22788         
22789        
22790     },
22791
22792     beforeBlur : function(){
22793         var v = this.parseDate(this.getRawValue());
22794         if(v){
22795             this.setValue(v);
22796         }
22797     }
22798
22799     /** @cfg {Boolean} grow @hide */
22800     /** @cfg {Number} growMin @hide */
22801     /** @cfg {Number} growMax @hide */
22802     /**
22803      * @hide
22804      * @method autoSize
22805      */
22806 });/*
22807  * Based on:
22808  * Ext JS Library 1.1.1
22809  * Copyright(c) 2006-2007, Ext JS, LLC.
22810  *
22811  * Originally Released Under LGPL - original licence link has changed is not relivant.
22812  *
22813  * Fork - LGPL
22814  * <script type="text/javascript">
22815  */
22816  
22817
22818 /**
22819  * @class Roo.form.ComboBox
22820  * @extends Roo.form.TriggerField
22821  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22822  * @constructor
22823  * Create a new ComboBox.
22824  * @param {Object} config Configuration options
22825  */
22826 Roo.form.ComboBox = function(config){
22827     Roo.form.ComboBox.superclass.constructor.call(this, config);
22828     this.addEvents({
22829         /**
22830          * @event expand
22831          * Fires when the dropdown list is expanded
22832              * @param {Roo.form.ComboBox} combo This combo box
22833              */
22834         'expand' : true,
22835         /**
22836          * @event collapse
22837          * Fires when the dropdown list is collapsed
22838              * @param {Roo.form.ComboBox} combo This combo box
22839              */
22840         'collapse' : true,
22841         /**
22842          * @event beforeselect
22843          * Fires before a list item is selected. Return false to cancel the selection.
22844              * @param {Roo.form.ComboBox} combo This combo box
22845              * @param {Roo.data.Record} record The data record returned from the underlying store
22846              * @param {Number} index The index of the selected item in the dropdown list
22847              */
22848         'beforeselect' : true,
22849         /**
22850          * @event select
22851          * Fires when a list item is selected
22852              * @param {Roo.form.ComboBox} combo This combo box
22853              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22854              * @param {Number} index The index of the selected item in the dropdown list
22855              */
22856         'select' : true,
22857         /**
22858          * @event beforequery
22859          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22860          * The event object passed has these properties:
22861              * @param {Roo.form.ComboBox} combo This combo box
22862              * @param {String} query The query
22863              * @param {Boolean} forceAll true to force "all" query
22864              * @param {Boolean} cancel true to cancel the query
22865              * @param {Object} e The query event object
22866              */
22867         'beforequery': true,
22868          /**
22869          * @event add
22870          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22871              * @param {Roo.form.ComboBox} combo This combo box
22872              */
22873         'add' : true,
22874         /**
22875          * @event edit
22876          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22877              * @param {Roo.form.ComboBox} combo This combo box
22878              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22879              */
22880         'edit' : true
22881         
22882         
22883     });
22884     if(this.transform){
22885         this.allowDomMove = false;
22886         var s = Roo.getDom(this.transform);
22887         if(!this.hiddenName){
22888             this.hiddenName = s.name;
22889         }
22890         if(!this.store){
22891             this.mode = 'local';
22892             var d = [], opts = s.options;
22893             for(var i = 0, len = opts.length;i < len; i++){
22894                 var o = opts[i];
22895                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22896                 if(o.selected) {
22897                     this.value = value;
22898                 }
22899                 d.push([value, o.text]);
22900             }
22901             this.store = new Roo.data.SimpleStore({
22902                 'id': 0,
22903                 fields: ['value', 'text'],
22904                 data : d
22905             });
22906             this.valueField = 'value';
22907             this.displayField = 'text';
22908         }
22909         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22910         if(!this.lazyRender){
22911             this.target = true;
22912             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22913             s.parentNode.removeChild(s); // remove it
22914             this.render(this.el.parentNode);
22915         }else{
22916             s.parentNode.removeChild(s); // remove it
22917         }
22918
22919     }
22920     if (this.store) {
22921         this.store = Roo.factory(this.store, Roo.data);
22922     }
22923     
22924     this.selectedIndex = -1;
22925     if(this.mode == 'local'){
22926         if(config.queryDelay === undefined){
22927             this.queryDelay = 10;
22928         }
22929         if(config.minChars === undefined){
22930             this.minChars = 0;
22931         }
22932     }
22933 };
22934
22935 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22936     /**
22937      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22938      */
22939     /**
22940      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22941      * rendering into an Roo.Editor, defaults to false)
22942      */
22943     /**
22944      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22945      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22946      */
22947     /**
22948      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22949      */
22950     /**
22951      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22952      * the dropdown list (defaults to undefined, with no header element)
22953      */
22954
22955      /**
22956      * @cfg {String/Roo.Template} tpl The template to use to render the output
22957      */
22958      
22959     // private
22960     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22961     /**
22962      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22963      */
22964     listWidth: undefined,
22965     /**
22966      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22967      * mode = 'remote' or 'text' if mode = 'local')
22968      */
22969     displayField: undefined,
22970     /**
22971      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22972      * mode = 'remote' or 'value' if mode = 'local'). 
22973      * Note: use of a valueField requires the user make a selection
22974      * in order for a value to be mapped.
22975      */
22976     valueField: undefined,
22977     
22978     
22979     /**
22980      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22981      * field's data value (defaults to the underlying DOM element's name)
22982      */
22983     hiddenName: undefined,
22984     /**
22985      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22986      */
22987     listClass: '',
22988     /**
22989      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22990      */
22991     selectedClass: 'x-combo-selected',
22992     /**
22993      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22994      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22995      * which displays a downward arrow icon).
22996      */
22997     triggerClass : 'x-form-arrow-trigger',
22998     /**
22999      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23000      */
23001     shadow:'sides',
23002     /**
23003      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23004      * anchor positions (defaults to 'tl-bl')
23005      */
23006     listAlign: 'tl-bl?',
23007     /**
23008      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23009      */
23010     maxHeight: 300,
23011     /**
23012      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23013      * query specified by the allQuery config option (defaults to 'query')
23014      */
23015     triggerAction: 'query',
23016     /**
23017      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23018      * (defaults to 4, does not apply if editable = false)
23019      */
23020     minChars : 4,
23021     /**
23022      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23023      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23024      */
23025     typeAhead: false,
23026     /**
23027      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23028      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23029      */
23030     queryDelay: 500,
23031     /**
23032      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23033      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23034      */
23035     pageSize: 0,
23036     /**
23037      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23038      * when editable = true (defaults to false)
23039      */
23040     selectOnFocus:false,
23041     /**
23042      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23043      */
23044     queryParam: 'query',
23045     /**
23046      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23047      * when mode = 'remote' (defaults to 'Loading...')
23048      */
23049     loadingText: 'Loading...',
23050     /**
23051      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23052      */
23053     resizable: false,
23054     /**
23055      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23056      */
23057     handleHeight : 8,
23058     /**
23059      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23060      * traditional select (defaults to true)
23061      */
23062     editable: true,
23063     /**
23064      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23065      */
23066     allQuery: '',
23067     /**
23068      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23069      */
23070     mode: 'remote',
23071     /**
23072      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23073      * listWidth has a higher value)
23074      */
23075     minListWidth : 70,
23076     /**
23077      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23078      * allow the user to set arbitrary text into the field (defaults to false)
23079      */
23080     forceSelection:false,
23081     /**
23082      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23083      * if typeAhead = true (defaults to 250)
23084      */
23085     typeAheadDelay : 250,
23086     /**
23087      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23088      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23089      */
23090     valueNotFoundText : undefined,
23091     /**
23092      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23093      */
23094     blockFocus : false,
23095     
23096     /**
23097      * @cfg {Boolean} disableClear Disable showing of clear button.
23098      */
23099     disableClear : false,
23100     /**
23101      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23102      */
23103     alwaysQuery : false,
23104     
23105     //private
23106     addicon : false,
23107     editicon: false,
23108     
23109     // element that contains real text value.. (when hidden is used..)
23110      
23111     // private
23112     onRender : function(ct, position){
23113         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23114         if(this.hiddenName){
23115             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23116                     'before', true);
23117             this.hiddenField.value =
23118                 this.hiddenValue !== undefined ? this.hiddenValue :
23119                 this.value !== undefined ? this.value : '';
23120
23121             // prevent input submission
23122             this.el.dom.removeAttribute('name');
23123              
23124              
23125         }
23126         if(Roo.isGecko){
23127             this.el.dom.setAttribute('autocomplete', 'off');
23128         }
23129
23130         var cls = 'x-combo-list';
23131
23132         this.list = new Roo.Layer({
23133             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23134         });
23135
23136         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23137         this.list.setWidth(lw);
23138         this.list.swallowEvent('mousewheel');
23139         this.assetHeight = 0;
23140
23141         if(this.title){
23142             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23143             this.assetHeight += this.header.getHeight();
23144         }
23145
23146         this.innerList = this.list.createChild({cls:cls+'-inner'});
23147         this.innerList.on('mouseover', this.onViewOver, this);
23148         this.innerList.on('mousemove', this.onViewMove, this);
23149         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23150         
23151         if(this.allowBlank && !this.pageSize && !this.disableClear){
23152             this.footer = this.list.createChild({cls:cls+'-ft'});
23153             this.pageTb = new Roo.Toolbar(this.footer);
23154            
23155         }
23156         if(this.pageSize){
23157             this.footer = this.list.createChild({cls:cls+'-ft'});
23158             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23159                     {pageSize: this.pageSize});
23160             
23161         }
23162         
23163         if (this.pageTb && this.allowBlank && !this.disableClear) {
23164             var _this = this;
23165             this.pageTb.add(new Roo.Toolbar.Fill(), {
23166                 cls: 'x-btn-icon x-btn-clear',
23167                 text: '&#160;',
23168                 handler: function()
23169                 {
23170                     _this.collapse();
23171                     _this.clearValue();
23172                     _this.onSelect(false, -1);
23173                 }
23174             });
23175         }
23176         if (this.footer) {
23177             this.assetHeight += this.footer.getHeight();
23178         }
23179         
23180
23181         if(!this.tpl){
23182             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23183         }
23184
23185         this.view = new Roo.View(this.innerList, this.tpl, {
23186             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23187         });
23188
23189         this.view.on('click', this.onViewClick, this);
23190
23191         this.store.on('beforeload', this.onBeforeLoad, this);
23192         this.store.on('load', this.onLoad, this);
23193         this.store.on('loadexception', this.onLoadException, this);
23194
23195         if(this.resizable){
23196             this.resizer = new Roo.Resizable(this.list,  {
23197                pinned:true, handles:'se'
23198             });
23199             this.resizer.on('resize', function(r, w, h){
23200                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23201                 this.listWidth = w;
23202                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23203                 this.restrictHeight();
23204             }, this);
23205             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23206         }
23207         if(!this.editable){
23208             this.editable = true;
23209             this.setEditable(false);
23210         }  
23211         
23212         
23213         if (typeof(this.events.add.listeners) != 'undefined') {
23214             
23215             this.addicon = this.wrap.createChild(
23216                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23217        
23218             this.addicon.on('click', function(e) {
23219                 this.fireEvent('add', this);
23220             }, this);
23221         }
23222         if (typeof(this.events.edit.listeners) != 'undefined') {
23223             
23224             this.editicon = this.wrap.createChild(
23225                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23226             if (this.addicon) {
23227                 this.editicon.setStyle('margin-left', '40px');
23228             }
23229             this.editicon.on('click', function(e) {
23230                 
23231                 // we fire even  if inothing is selected..
23232                 this.fireEvent('edit', this, this.lastData );
23233                 
23234             }, this);
23235         }
23236         
23237         
23238         
23239     },
23240
23241     // private
23242     initEvents : function(){
23243         Roo.form.ComboBox.superclass.initEvents.call(this);
23244
23245         this.keyNav = new Roo.KeyNav(this.el, {
23246             "up" : function(e){
23247                 this.inKeyMode = true;
23248                 this.selectPrev();
23249             },
23250
23251             "down" : function(e){
23252                 if(!this.isExpanded()){
23253                     this.onTriggerClick();
23254                 }else{
23255                     this.inKeyMode = true;
23256                     this.selectNext();
23257                 }
23258             },
23259
23260             "enter" : function(e){
23261                 this.onViewClick();
23262                 //return true;
23263             },
23264
23265             "esc" : function(e){
23266                 this.collapse();
23267             },
23268
23269             "tab" : function(e){
23270                 this.onViewClick(false);
23271                 this.fireEvent("specialkey", this, e);
23272                 return true;
23273             },
23274
23275             scope : this,
23276
23277             doRelay : function(foo, bar, hname){
23278                 if(hname == 'down' || this.scope.isExpanded()){
23279                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23280                 }
23281                 return true;
23282             },
23283
23284             forceKeyDown: true
23285         });
23286         this.queryDelay = Math.max(this.queryDelay || 10,
23287                 this.mode == 'local' ? 10 : 250);
23288         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23289         if(this.typeAhead){
23290             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23291         }
23292         if(this.editable !== false){
23293             this.el.on("keyup", this.onKeyUp, this);
23294         }
23295         if(this.forceSelection){
23296             this.on('blur', this.doForce, this);
23297         }
23298     },
23299
23300     onDestroy : function(){
23301         if(this.view){
23302             this.view.setStore(null);
23303             this.view.el.removeAllListeners();
23304             this.view.el.remove();
23305             this.view.purgeListeners();
23306         }
23307         if(this.list){
23308             this.list.destroy();
23309         }
23310         if(this.store){
23311             this.store.un('beforeload', this.onBeforeLoad, this);
23312             this.store.un('load', this.onLoad, this);
23313             this.store.un('loadexception', this.onLoadException, this);
23314         }
23315         Roo.form.ComboBox.superclass.onDestroy.call(this);
23316     },
23317
23318     // private
23319     fireKey : function(e){
23320         if(e.isNavKeyPress() && !this.list.isVisible()){
23321             this.fireEvent("specialkey", this, e);
23322         }
23323     },
23324
23325     // private
23326     onResize: function(w, h){
23327         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23328         
23329         if(typeof w != 'number'){
23330             // we do not handle it!?!?
23331             return;
23332         }
23333         var tw = this.trigger.getWidth();
23334         tw += this.addicon ? this.addicon.getWidth() : 0;
23335         tw += this.editicon ? this.editicon.getWidth() : 0;
23336         var x = w - tw;
23337         this.el.setWidth( this.adjustWidth('input', x));
23338             
23339         this.trigger.setStyle('left', x+'px');
23340         
23341         if(this.list && this.listWidth === undefined){
23342             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23343             this.list.setWidth(lw);
23344             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23345         }
23346         
23347     
23348         
23349     },
23350
23351     /**
23352      * Allow or prevent the user from directly editing the field text.  If false is passed,
23353      * the user will only be able to select from the items defined in the dropdown list.  This method
23354      * is the runtime equivalent of setting the 'editable' config option at config time.
23355      * @param {Boolean} value True to allow the user to directly edit the field text
23356      */
23357     setEditable : function(value){
23358         if(value == this.editable){
23359             return;
23360         }
23361         this.editable = value;
23362         if(!value){
23363             this.el.dom.setAttribute('readOnly', true);
23364             this.el.on('mousedown', this.onTriggerClick,  this);
23365             this.el.addClass('x-combo-noedit');
23366         }else{
23367             this.el.dom.setAttribute('readOnly', false);
23368             this.el.un('mousedown', this.onTriggerClick,  this);
23369             this.el.removeClass('x-combo-noedit');
23370         }
23371     },
23372
23373     // private
23374     onBeforeLoad : function(){
23375         if(!this.hasFocus){
23376             return;
23377         }
23378         this.innerList.update(this.loadingText ?
23379                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23380         this.restrictHeight();
23381         this.selectedIndex = -1;
23382     },
23383
23384     // private
23385     onLoad : function(){
23386         if(!this.hasFocus){
23387             return;
23388         }
23389         if(this.store.getCount() > 0){
23390             this.expand();
23391             this.restrictHeight();
23392             if(this.lastQuery == this.allQuery){
23393                 if(this.editable){
23394                     this.el.dom.select();
23395                 }
23396                 if(!this.selectByValue(this.value, true)){
23397                     this.select(0, true);
23398                 }
23399             }else{
23400                 this.selectNext();
23401                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23402                     this.taTask.delay(this.typeAheadDelay);
23403                 }
23404             }
23405         }else{
23406             this.onEmptyResults();
23407         }
23408         //this.el.focus();
23409     },
23410     // private
23411     onLoadException : function()
23412     {
23413         this.collapse();
23414         Roo.log(this.store.reader.jsonData);
23415         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23416             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23417         }
23418         
23419         
23420     },
23421     // private
23422     onTypeAhead : function(){
23423         if(this.store.getCount() > 0){
23424             var r = this.store.getAt(0);
23425             var newValue = r.data[this.displayField];
23426             var len = newValue.length;
23427             var selStart = this.getRawValue().length;
23428             if(selStart != len){
23429                 this.setRawValue(newValue);
23430                 this.selectText(selStart, newValue.length);
23431             }
23432         }
23433     },
23434
23435     // private
23436     onSelect : function(record, index){
23437         if(this.fireEvent('beforeselect', this, record, index) !== false){
23438             this.setFromData(index > -1 ? record.data : false);
23439             this.collapse();
23440             this.fireEvent('select', this, record, index);
23441         }
23442     },
23443
23444     /**
23445      * Returns the currently selected field value or empty string if no value is set.
23446      * @return {String} value The selected value
23447      */
23448     getValue : function(){
23449         if(this.valueField){
23450             return typeof this.value != 'undefined' ? this.value : '';
23451         }
23452         return Roo.form.ComboBox.superclass.getValue.call(this);
23453     },
23454
23455     /**
23456      * Clears any text/value currently set in the field
23457      */
23458     clearValue : function(){
23459         if(this.hiddenField){
23460             this.hiddenField.value = '';
23461         }
23462         this.value = '';
23463         this.setRawValue('');
23464         this.lastSelectionText = '';
23465         
23466     },
23467
23468     /**
23469      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23470      * will be displayed in the field.  If the value does not match the data value of an existing item,
23471      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23472      * Otherwise the field will be blank (although the value will still be set).
23473      * @param {String} value The value to match
23474      */
23475     setValue : function(v){
23476         var text = v;
23477         if(this.valueField){
23478             var r = this.findRecord(this.valueField, v);
23479             if(r){
23480                 text = r.data[this.displayField];
23481             }else if(this.valueNotFoundText !== undefined){
23482                 text = this.valueNotFoundText;
23483             }
23484         }
23485         this.lastSelectionText = text;
23486         if(this.hiddenField){
23487             this.hiddenField.value = v;
23488         }
23489         Roo.form.ComboBox.superclass.setValue.call(this, text);
23490         this.value = v;
23491     },
23492     /**
23493      * @property {Object} the last set data for the element
23494      */
23495     
23496     lastData : false,
23497     /**
23498      * Sets the value of the field based on a object which is related to the record format for the store.
23499      * @param {Object} value the value to set as. or false on reset?
23500      */
23501     setFromData : function(o){
23502         var dv = ''; // display value
23503         var vv = ''; // value value..
23504         this.lastData = o;
23505         if (this.displayField) {
23506             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23507         } else {
23508             // this is an error condition!!!
23509             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23510         }
23511         
23512         if(this.valueField){
23513             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23514         }
23515         if(this.hiddenField){
23516             this.hiddenField.value = vv;
23517             
23518             this.lastSelectionText = dv;
23519             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23520             this.value = vv;
23521             return;
23522         }
23523         // no hidden field.. - we store the value in 'value', but still display
23524         // display field!!!!
23525         this.lastSelectionText = dv;
23526         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23527         this.value = vv;
23528         
23529         
23530     },
23531     // private
23532     reset : function(){
23533         // overridden so that last data is reset..
23534         this.setValue(this.resetValue);
23535         this.clearInvalid();
23536         this.lastData = false;
23537         if (this.view) {
23538             this.view.clearSelections();
23539         }
23540     },
23541     // private
23542     findRecord : function(prop, value){
23543         var record;
23544         if(this.store.getCount() > 0){
23545             this.store.each(function(r){
23546                 if(r.data[prop] == value){
23547                     record = r;
23548                     return false;
23549                 }
23550                 return true;
23551             });
23552         }
23553         return record;
23554     },
23555     
23556     getName: function()
23557     {
23558         // returns hidden if it's set..
23559         if (!this.rendered) {return ''};
23560         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23561         
23562     },
23563     // private
23564     onViewMove : function(e, t){
23565         this.inKeyMode = false;
23566     },
23567
23568     // private
23569     onViewOver : function(e, t){
23570         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23571             return;
23572         }
23573         var item = this.view.findItemFromChild(t);
23574         if(item){
23575             var index = this.view.indexOf(item);
23576             this.select(index, false);
23577         }
23578     },
23579
23580     // private
23581     onViewClick : function(doFocus)
23582     {
23583         var index = this.view.getSelectedIndexes()[0];
23584         var r = this.store.getAt(index);
23585         if(r){
23586             this.onSelect(r, index);
23587         }
23588         if(doFocus !== false && !this.blockFocus){
23589             this.el.focus();
23590         }
23591     },
23592
23593     // private
23594     restrictHeight : function(){
23595         this.innerList.dom.style.height = '';
23596         var inner = this.innerList.dom;
23597         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23598         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23599         this.list.beginUpdate();
23600         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23601         this.list.alignTo(this.el, this.listAlign);
23602         this.list.endUpdate();
23603     },
23604
23605     // private
23606     onEmptyResults : function(){
23607         this.collapse();
23608     },
23609
23610     /**
23611      * Returns true if the dropdown list is expanded, else false.
23612      */
23613     isExpanded : function(){
23614         return this.list.isVisible();
23615     },
23616
23617     /**
23618      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23619      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23620      * @param {String} value The data value of the item to select
23621      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23622      * selected item if it is not currently in view (defaults to true)
23623      * @return {Boolean} True if the value matched an item in the list, else false
23624      */
23625     selectByValue : function(v, scrollIntoView){
23626         if(v !== undefined && v !== null){
23627             var r = this.findRecord(this.valueField || this.displayField, v);
23628             if(r){
23629                 this.select(this.store.indexOf(r), scrollIntoView);
23630                 return true;
23631             }
23632         }
23633         return false;
23634     },
23635
23636     /**
23637      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23638      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23639      * @param {Number} index The zero-based index of the list item to select
23640      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23641      * selected item if it is not currently in view (defaults to true)
23642      */
23643     select : function(index, scrollIntoView){
23644         this.selectedIndex = index;
23645         this.view.select(index);
23646         if(scrollIntoView !== false){
23647             var el = this.view.getNode(index);
23648             if(el){
23649                 this.innerList.scrollChildIntoView(el, false);
23650             }
23651         }
23652     },
23653
23654     // private
23655     selectNext : function(){
23656         var ct = this.store.getCount();
23657         if(ct > 0){
23658             if(this.selectedIndex == -1){
23659                 this.select(0);
23660             }else if(this.selectedIndex < ct-1){
23661                 this.select(this.selectedIndex+1);
23662             }
23663         }
23664     },
23665
23666     // private
23667     selectPrev : function(){
23668         var ct = this.store.getCount();
23669         if(ct > 0){
23670             if(this.selectedIndex == -1){
23671                 this.select(0);
23672             }else if(this.selectedIndex != 0){
23673                 this.select(this.selectedIndex-1);
23674             }
23675         }
23676     },
23677
23678     // private
23679     onKeyUp : function(e){
23680         if(this.editable !== false && !e.isSpecialKey()){
23681             this.lastKey = e.getKey();
23682             this.dqTask.delay(this.queryDelay);
23683         }
23684     },
23685
23686     // private
23687     validateBlur : function(){
23688         return !this.list || !this.list.isVisible();   
23689     },
23690
23691     // private
23692     initQuery : function(){
23693         this.doQuery(this.getRawValue());
23694     },
23695
23696     // private
23697     doForce : function(){
23698         if(this.el.dom.value.length > 0){
23699             this.el.dom.value =
23700                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23701              
23702         }
23703     },
23704
23705     /**
23706      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23707      * query allowing the query action to be canceled if needed.
23708      * @param {String} query The SQL query to execute
23709      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23710      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23711      * saved in the current store (defaults to false)
23712      */
23713     doQuery : function(q, forceAll){
23714         if(q === undefined || q === null){
23715             q = '';
23716         }
23717         var qe = {
23718             query: q,
23719             forceAll: forceAll,
23720             combo: this,
23721             cancel:false
23722         };
23723         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23724             return false;
23725         }
23726         q = qe.query;
23727         forceAll = qe.forceAll;
23728         if(forceAll === true || (q.length >= this.minChars)){
23729             if(this.lastQuery != q || this.alwaysQuery){
23730                 this.lastQuery = q;
23731                 if(this.mode == 'local'){
23732                     this.selectedIndex = -1;
23733                     if(forceAll){
23734                         this.store.clearFilter();
23735                     }else{
23736                         this.store.filter(this.displayField, q);
23737                     }
23738                     this.onLoad();
23739                 }else{
23740                     this.store.baseParams[this.queryParam] = q;
23741                     this.store.load({
23742                         params: this.getParams(q)
23743                     });
23744                     this.expand();
23745                 }
23746             }else{
23747                 this.selectedIndex = -1;
23748                 this.onLoad();   
23749             }
23750         }
23751     },
23752
23753     // private
23754     getParams : function(q){
23755         var p = {};
23756         //p[this.queryParam] = q;
23757         if(this.pageSize){
23758             p.start = 0;
23759             p.limit = this.pageSize;
23760         }
23761         return p;
23762     },
23763
23764     /**
23765      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23766      */
23767     collapse : function(){
23768         if(!this.isExpanded()){
23769             return;
23770         }
23771         this.list.hide();
23772         Roo.get(document).un('mousedown', this.collapseIf, this);
23773         Roo.get(document).un('mousewheel', this.collapseIf, this);
23774         if (!this.editable) {
23775             Roo.get(document).un('keydown', this.listKeyPress, this);
23776         }
23777         this.fireEvent('collapse', this);
23778     },
23779
23780     // private
23781     collapseIf : function(e){
23782         if(!e.within(this.wrap) && !e.within(this.list)){
23783             this.collapse();
23784         }
23785     },
23786
23787     /**
23788      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23789      */
23790     expand : function(){
23791         if(this.isExpanded() || !this.hasFocus){
23792             return;
23793         }
23794         this.list.alignTo(this.el, this.listAlign);
23795         this.list.show();
23796         Roo.get(document).on('mousedown', this.collapseIf, this);
23797         Roo.get(document).on('mousewheel', this.collapseIf, this);
23798         if (!this.editable) {
23799             Roo.get(document).on('keydown', this.listKeyPress, this);
23800         }
23801         
23802         this.fireEvent('expand', this);
23803     },
23804
23805     // private
23806     // Implements the default empty TriggerField.onTriggerClick function
23807     onTriggerClick : function(){
23808         if(this.disabled){
23809             return;
23810         }
23811         if(this.isExpanded()){
23812             this.collapse();
23813             if (!this.blockFocus) {
23814                 this.el.focus();
23815             }
23816             
23817         }else {
23818             this.hasFocus = true;
23819             if(this.triggerAction == 'all') {
23820                 this.doQuery(this.allQuery, true);
23821             } else {
23822                 this.doQuery(this.getRawValue());
23823             }
23824             if (!this.blockFocus) {
23825                 this.el.focus();
23826             }
23827         }
23828     },
23829     listKeyPress : function(e)
23830     {
23831         //Roo.log('listkeypress');
23832         // scroll to first matching element based on key pres..
23833         if (e.isSpecialKey()) {
23834             return false;
23835         }
23836         var k = String.fromCharCode(e.getKey()).toUpperCase();
23837         //Roo.log(k);
23838         var match  = false;
23839         var csel = this.view.getSelectedNodes();
23840         var cselitem = false;
23841         if (csel.length) {
23842             var ix = this.view.indexOf(csel[0]);
23843             cselitem  = this.store.getAt(ix);
23844             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23845                 cselitem = false;
23846             }
23847             
23848         }
23849         
23850         this.store.each(function(v) { 
23851             if (cselitem) {
23852                 // start at existing selection.
23853                 if (cselitem.id == v.id) {
23854                     cselitem = false;
23855                 }
23856                 return;
23857             }
23858                 
23859             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23860                 match = this.store.indexOf(v);
23861                 return false;
23862             }
23863         }, this);
23864         
23865         if (match === false) {
23866             return true; // no more action?
23867         }
23868         // scroll to?
23869         this.view.select(match);
23870         var sn = Roo.get(this.view.getSelectedNodes()[0])
23871         sn.scrollIntoView(sn.dom.parentNode, false);
23872     }
23873
23874     /** 
23875     * @cfg {Boolean} grow 
23876     * @hide 
23877     */
23878     /** 
23879     * @cfg {Number} growMin 
23880     * @hide 
23881     */
23882     /** 
23883     * @cfg {Number} growMax 
23884     * @hide 
23885     */
23886     /**
23887      * @hide
23888      * @method autoSize
23889      */
23890 });/*
23891  * Copyright(c) 2010-2012, Roo J Solutions Limited
23892  *
23893  * Licence LGPL
23894  *
23895  */
23896
23897 /**
23898  * @class Roo.form.ComboBoxArray
23899  * @extends Roo.form.TextField
23900  * A facebook style adder... for lists of email / people / countries  etc...
23901  * pick multiple items from a combo box, and shows each one.
23902  *
23903  *  Fred [x]  Brian [x]  [Pick another |v]
23904  *
23905  *
23906  *  For this to work: it needs various extra information
23907  *    - normal combo problay has
23908  *      name, hiddenName
23909  *    + displayField, valueField
23910  *
23911  *    For our purpose...
23912  *
23913  *
23914  *   If we change from 'extends' to wrapping...
23915  *   
23916  *  
23917  *
23918  
23919  
23920  * @constructor
23921  * Create a new ComboBoxArray.
23922  * @param {Object} config Configuration options
23923  */
23924  
23925
23926 Roo.form.ComboBoxArray = function(config)
23927 {
23928     this.addEvents({
23929         /**
23930          * @event beforeremove
23931          * Fires before remove the value from the list
23932              * @param {Roo.form.ComboBoxArray} _self This combo box array
23933              * @param {Roo.form.ComboBoxArray.Item} item removed item
23934              */
23935         'beforeremove' : true,
23936         /**
23937          * @event remove
23938          * Fires when remove the value from the list
23939              * @param {Roo.form.ComboBoxArray} _self This combo box array
23940              * @param {Roo.form.ComboBoxArray.Item} item removed item
23941              */
23942         'remove' : true
23943         
23944         
23945     });
23946     
23947     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23948     
23949     this.items = new Roo.util.MixedCollection(false);
23950     
23951     // construct the child combo...
23952     
23953     
23954     
23955     
23956    
23957     
23958 }
23959
23960  
23961 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23962
23963     /**
23964      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23965      */
23966     
23967     lastData : false,
23968     
23969     // behavies liek a hiddne field
23970     inputType:      'hidden',
23971     /**
23972      * @cfg {Number} width The width of the box that displays the selected element
23973      */ 
23974     width:          300,
23975
23976     
23977     
23978     /**
23979      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23980      */
23981     name : false,
23982     /**
23983      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23984      */
23985     hiddenName : false,
23986     
23987     
23988     // private the array of items that are displayed..
23989     items  : false,
23990     // private - the hidden field el.
23991     hiddenEl : false,
23992     // private - the filed el..
23993     el : false,
23994     
23995     //validateValue : function() { return true; }, // all values are ok!
23996     //onAddClick: function() { },
23997     
23998     onRender : function(ct, position) 
23999     {
24000         
24001         // create the standard hidden element
24002         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24003         
24004         
24005         // give fake names to child combo;
24006         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24007         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24008         
24009         this.combo = Roo.factory(this.combo, Roo.form);
24010         this.combo.onRender(ct, position);
24011         if (typeof(this.combo.width) != 'undefined') {
24012             this.combo.onResize(this.combo.width,0);
24013         }
24014         
24015         this.combo.initEvents();
24016         
24017         // assigned so form know we need to do this..
24018         this.store          = this.combo.store;
24019         this.valueField     = this.combo.valueField;
24020         this.displayField   = this.combo.displayField ;
24021         
24022         
24023         this.combo.wrap.addClass('x-cbarray-grp');
24024         
24025         var cbwrap = this.combo.wrap.createChild(
24026             {tag: 'div', cls: 'x-cbarray-cb'},
24027             this.combo.el.dom
24028         );
24029         
24030              
24031         this.hiddenEl = this.combo.wrap.createChild({
24032             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24033         });
24034         this.el = this.combo.wrap.createChild({
24035             tag: 'input',  type:'hidden' , name: this.name, value : ''
24036         });
24037          //   this.el.dom.removeAttribute("name");
24038         
24039         
24040         this.outerWrap = this.combo.wrap;
24041         this.wrap = cbwrap;
24042         
24043         this.outerWrap.setWidth(this.width);
24044         this.outerWrap.dom.removeChild(this.el.dom);
24045         
24046         this.wrap.dom.appendChild(this.el.dom);
24047         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24048         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24049         
24050         this.combo.trigger.setStyle('position','relative');
24051         this.combo.trigger.setStyle('left', '0px');
24052         this.combo.trigger.setStyle('top', '2px');
24053         
24054         this.combo.el.setStyle('vertical-align', 'text-bottom');
24055         
24056         //this.trigger.setStyle('vertical-align', 'top');
24057         
24058         // this should use the code from combo really... on('add' ....)
24059         if (this.adder) {
24060             
24061         
24062             this.adder = this.outerWrap.createChild(
24063                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24064             var _t = this;
24065             this.adder.on('click', function(e) {
24066                 _t.fireEvent('adderclick', this, e);
24067             }, _t);
24068         }
24069         //var _t = this;
24070         //this.adder.on('click', this.onAddClick, _t);
24071         
24072         
24073         this.combo.on('select', function(cb, rec, ix) {
24074             this.addItem(rec.data);
24075             
24076             cb.setValue('');
24077             cb.el.dom.value = '';
24078             //cb.lastData = rec.data;
24079             // add to list
24080             
24081         }, this);
24082         
24083         
24084     },
24085     
24086     
24087     getName: function()
24088     {
24089         // returns hidden if it's set..
24090         if (!this.rendered) {return ''};
24091         return  this.hiddenName ? this.hiddenName : this.name;
24092         
24093     },
24094     
24095     
24096     onResize: function(w, h){
24097         
24098         return;
24099         // not sure if this is needed..
24100         //this.combo.onResize(w,h);
24101         
24102         if(typeof w != 'number'){
24103             // we do not handle it!?!?
24104             return;
24105         }
24106         var tw = this.combo.trigger.getWidth();
24107         tw += this.addicon ? this.addicon.getWidth() : 0;
24108         tw += this.editicon ? this.editicon.getWidth() : 0;
24109         var x = w - tw;
24110         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24111             
24112         this.combo.trigger.setStyle('left', '0px');
24113         
24114         if(this.list && this.listWidth === undefined){
24115             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24116             this.list.setWidth(lw);
24117             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24118         }
24119         
24120     
24121         
24122     },
24123     
24124     addItem: function(rec)
24125     {
24126         var valueField = this.combo.valueField;
24127         var displayField = this.combo.displayField;
24128         if (this.items.indexOfKey(rec[valueField]) > -1) {
24129             //console.log("GOT " + rec.data.id);
24130             return;
24131         }
24132         
24133         var x = new Roo.form.ComboBoxArray.Item({
24134             //id : rec[this.idField],
24135             data : rec,
24136             displayField : displayField ,
24137             tipField : displayField ,
24138             cb : this
24139         });
24140         // use the 
24141         this.items.add(rec[valueField],x);
24142         // add it before the element..
24143         this.updateHiddenEl();
24144         x.render(this.outerWrap, this.wrap.dom);
24145         // add the image handler..
24146     },
24147     
24148     updateHiddenEl : function()
24149     {
24150         this.validate();
24151         if (!this.hiddenEl) {
24152             return;
24153         }
24154         var ar = [];
24155         var idField = this.combo.valueField;
24156         
24157         this.items.each(function(f) {
24158             ar.push(f.data[idField]);
24159            
24160         });
24161         this.hiddenEl.dom.value = ar.join(',');
24162         this.validate();
24163     },
24164     
24165     reset : function()
24166     {
24167         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24168         this.items.each(function(f) {
24169            f.remove(); 
24170         });
24171         this.el.dom.value = '';
24172         if (this.hiddenEl) {
24173             this.hiddenEl.dom.value = '';
24174         }
24175         
24176     },
24177     getValue: function()
24178     {
24179         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24180     },
24181     setValue: function(v) // not a valid action - must use addItems..
24182     {
24183          
24184         this.reset();
24185         
24186         
24187         
24188         if (this.store.isLocal && (typeof(v) == 'string')) {
24189             // then we can use the store to find the values..
24190             // comma seperated at present.. this needs to allow JSON based encoding..
24191             this.hiddenEl.value  = v;
24192             var v_ar = [];
24193             Roo.each(v.split(','), function(k) {
24194                 Roo.log("CHECK " + this.valueField + ',' + k);
24195                 var li = this.store.query(this.valueField, k);
24196                 if (!li.length) {
24197                     return;
24198                 }
24199                 var add = {};
24200                 add[this.valueField] = k;
24201                 add[this.displayField] = li.item(0).data[this.displayField];
24202                 
24203                 this.addItem(add);
24204             }, this) 
24205              
24206         }
24207         if (typeof(v) == 'object' ) {
24208             // then let's assume it's an array of objects..
24209             Roo.each(v, function(l) {
24210                 this.addItem(l);
24211             }, this);
24212              
24213         }
24214         
24215         
24216     },
24217     setFromData: function(v)
24218     {
24219         // this recieves an object, if setValues is called.
24220         this.reset();
24221         this.el.dom.value = v[this.displayField];
24222         this.hiddenEl.dom.value = v[this.valueField];
24223         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24224             return;
24225         }
24226         var kv = v[this.valueField];
24227         var dv = v[this.displayField];
24228         kv = typeof(kv) != 'string' ? '' : kv;
24229         dv = typeof(dv) != 'string' ? '' : dv;
24230         
24231         
24232         var keys = kv.split(',');
24233         var display = dv.split(',');
24234         for (var i = 0 ; i < keys.length; i++) {
24235             
24236             add = {};
24237             add[this.valueField] = keys[i];
24238             add[this.displayField] = display[i];
24239             this.addItem(add);
24240         }
24241       
24242         
24243     },
24244     
24245     /**
24246      * Validates the combox array value
24247      * @return {Boolean} True if the value is valid, else false
24248      */
24249     validate : function(){
24250         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24251             this.clearInvalid();
24252             return true;
24253         }
24254         return false;
24255     },
24256     
24257     validateValue : function(value){
24258         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24259         
24260     },
24261     
24262     /*@
24263      * overide
24264      * 
24265      */
24266     isDirty : function() {
24267         if(this.disabled) {
24268             return false;
24269         }
24270         
24271         try {
24272             var d = Roo.decode(String(this.originalValue));
24273         } catch (e) {
24274             return String(this.getValue()) !== String(this.originalValue);
24275         }
24276         
24277         var originalValue = [];
24278         
24279         for (var i = 0; i < d.length; i++){
24280             originalValue.push(d[i][this.valueField]);
24281         }
24282         
24283         return String(this.getValue()) !== String(originalValue.join(','));
24284         
24285     }
24286     
24287 });
24288
24289
24290
24291 /**
24292  * @class Roo.form.ComboBoxArray.Item
24293  * @extends Roo.BoxComponent
24294  * A selected item in the list
24295  *  Fred [x]  Brian [x]  [Pick another |v]
24296  * 
24297  * @constructor
24298  * Create a new item.
24299  * @param {Object} config Configuration options
24300  */
24301  
24302 Roo.form.ComboBoxArray.Item = function(config) {
24303     config.id = Roo.id();
24304     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24305 }
24306
24307 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24308     data : {},
24309     cb: false,
24310     displayField : false,
24311     tipField : false,
24312     
24313     
24314     defaultAutoCreate : {
24315         tag: 'div',
24316         cls: 'x-cbarray-item',
24317         cn : [ 
24318             { tag: 'div' },
24319             {
24320                 tag: 'img',
24321                 width:16,
24322                 height : 16,
24323                 src : Roo.BLANK_IMAGE_URL ,
24324                 align: 'center'
24325             }
24326         ]
24327         
24328     },
24329     
24330  
24331     onRender : function(ct, position)
24332     {
24333         Roo.form.Field.superclass.onRender.call(this, ct, position);
24334         
24335         if(!this.el){
24336             var cfg = this.getAutoCreate();
24337             this.el = ct.createChild(cfg, position);
24338         }
24339         
24340         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24341         
24342         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24343             this.cb.renderer(this.data) :
24344             String.format('{0}',this.data[this.displayField]);
24345         
24346             
24347         this.el.child('div').dom.setAttribute('qtip',
24348                         String.format('{0}',this.data[this.tipField])
24349         );
24350         
24351         this.el.child('img').on('click', this.remove, this);
24352         
24353     },
24354    
24355     remove : function()
24356     {
24357         if(this.cb.disabled){
24358             return;
24359         }
24360         
24361         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24362             this.cb.items.remove(this);
24363             this.el.child('img').un('click', this.remove, this);
24364             this.el.remove();
24365             this.cb.updateHiddenEl();
24366
24367             this.cb.fireEvent('remove', this.cb, this);
24368         }
24369         
24370     }
24371 });/*
24372  * Based on:
24373  * Ext JS Library 1.1.1
24374  * Copyright(c) 2006-2007, Ext JS, LLC.
24375  *
24376  * Originally Released Under LGPL - original licence link has changed is not relivant.
24377  *
24378  * Fork - LGPL
24379  * <script type="text/javascript">
24380  */
24381 /**
24382  * @class Roo.form.Checkbox
24383  * @extends Roo.form.Field
24384  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24385  * @constructor
24386  * Creates a new Checkbox
24387  * @param {Object} config Configuration options
24388  */
24389 Roo.form.Checkbox = function(config){
24390     Roo.form.Checkbox.superclass.constructor.call(this, config);
24391     this.addEvents({
24392         /**
24393          * @event check
24394          * Fires when the checkbox is checked or unchecked.
24395              * @param {Roo.form.Checkbox} this This checkbox
24396              * @param {Boolean} checked The new checked value
24397              */
24398         check : true
24399     });
24400 };
24401
24402 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24403     /**
24404      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24405      */
24406     focusClass : undefined,
24407     /**
24408      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24409      */
24410     fieldClass: "x-form-field",
24411     /**
24412      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24413      */
24414     checked: false,
24415     /**
24416      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24417      * {tag: "input", type: "checkbox", autocomplete: "off"})
24418      */
24419     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24420     /**
24421      * @cfg {String} boxLabel The text that appears beside the checkbox
24422      */
24423     boxLabel : "",
24424     /**
24425      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24426      */  
24427     inputValue : '1',
24428     /**
24429      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24430      */
24431      valueOff: '0', // value when not checked..
24432
24433     actionMode : 'viewEl', 
24434     //
24435     // private
24436     itemCls : 'x-menu-check-item x-form-item',
24437     groupClass : 'x-menu-group-item',
24438     inputType : 'hidden',
24439     
24440     
24441     inSetChecked: false, // check that we are not calling self...
24442     
24443     inputElement: false, // real input element?
24444     basedOn: false, // ????
24445     
24446     isFormField: true, // not sure where this is needed!!!!
24447
24448     onResize : function(){
24449         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24450         if(!this.boxLabel){
24451             this.el.alignTo(this.wrap, 'c-c');
24452         }
24453     },
24454
24455     initEvents : function(){
24456         Roo.form.Checkbox.superclass.initEvents.call(this);
24457         this.el.on("click", this.onClick,  this);
24458         this.el.on("change", this.onClick,  this);
24459     },
24460
24461
24462     getResizeEl : function(){
24463         return this.wrap;
24464     },
24465
24466     getPositionEl : function(){
24467         return this.wrap;
24468     },
24469
24470     // private
24471     onRender : function(ct, position){
24472         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24473         /*
24474         if(this.inputValue !== undefined){
24475             this.el.dom.value = this.inputValue;
24476         }
24477         */
24478         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24479         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24480         var viewEl = this.wrap.createChild({ 
24481             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24482         this.viewEl = viewEl;   
24483         this.wrap.on('click', this.onClick,  this); 
24484         
24485         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24486         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24487         
24488         
24489         
24490         if(this.boxLabel){
24491             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24492         //    viewEl.on('click', this.onClick,  this); 
24493         }
24494         //if(this.checked){
24495             this.setChecked(this.checked);
24496         //}else{
24497             //this.checked = this.el.dom;
24498         //}
24499
24500     },
24501
24502     // private
24503     initValue : Roo.emptyFn,
24504
24505     /**
24506      * Returns the checked state of the checkbox.
24507      * @return {Boolean} True if checked, else false
24508      */
24509     getValue : function(){
24510         if(this.el){
24511             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24512         }
24513         return this.valueOff;
24514         
24515     },
24516
24517         // private
24518     onClick : function(){ 
24519         if (this.disabled) {
24520             return;
24521         }
24522         this.setChecked(!this.checked);
24523
24524         //if(this.el.dom.checked != this.checked){
24525         //    this.setValue(this.el.dom.checked);
24526        // }
24527     },
24528
24529     /**
24530      * Sets the checked state of the checkbox.
24531      * On is always based on a string comparison between inputValue and the param.
24532      * @param {Boolean/String} value - the value to set 
24533      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24534      */
24535     setValue : function(v,suppressEvent){
24536         
24537         
24538         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24539         //if(this.el && this.el.dom){
24540         //    this.el.dom.checked = this.checked;
24541         //    this.el.dom.defaultChecked = this.checked;
24542         //}
24543         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24544         //this.fireEvent("check", this, this.checked);
24545     },
24546     // private..
24547     setChecked : function(state,suppressEvent)
24548     {
24549         if (this.inSetChecked) {
24550             this.checked = state;
24551             return;
24552         }
24553         
24554     
24555         if(this.wrap){
24556             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24557         }
24558         this.checked = state;
24559         if(suppressEvent !== true){
24560             this.fireEvent('check', this, state);
24561         }
24562         this.inSetChecked = true;
24563         this.el.dom.value = state ? this.inputValue : this.valueOff;
24564         this.inSetChecked = false;
24565         
24566     },
24567     // handle setting of hidden value by some other method!!?!?
24568     setFromHidden: function()
24569     {
24570         if(!this.el){
24571             return;
24572         }
24573         //console.log("SET FROM HIDDEN");
24574         //alert('setFrom hidden');
24575         this.setValue(this.el.dom.value);
24576     },
24577     
24578     onDestroy : function()
24579     {
24580         if(this.viewEl){
24581             Roo.get(this.viewEl).remove();
24582         }
24583          
24584         Roo.form.Checkbox.superclass.onDestroy.call(this);
24585     }
24586
24587 });/*
24588  * Based on:
24589  * Ext JS Library 1.1.1
24590  * Copyright(c) 2006-2007, Ext JS, LLC.
24591  *
24592  * Originally Released Under LGPL - original licence link has changed is not relivant.
24593  *
24594  * Fork - LGPL
24595  * <script type="text/javascript">
24596  */
24597  
24598 /**
24599  * @class Roo.form.Radio
24600  * @extends Roo.form.Checkbox
24601  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24602  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24603  * @constructor
24604  * Creates a new Radio
24605  * @param {Object} config Configuration options
24606  */
24607 Roo.form.Radio = function(){
24608     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24609 };
24610 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24611     inputType: 'radio',
24612
24613     /**
24614      * If this radio is part of a group, it will return the selected value
24615      * @return {String}
24616      */
24617     getGroupValue : function(){
24618         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24619     },
24620     
24621     
24622     onRender : function(ct, position){
24623         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24624         
24625         if(this.inputValue !== undefined){
24626             this.el.dom.value = this.inputValue;
24627         }
24628          
24629         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24630         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24631         //var viewEl = this.wrap.createChild({ 
24632         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24633         //this.viewEl = viewEl;   
24634         //this.wrap.on('click', this.onClick,  this); 
24635         
24636         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24637         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24638         
24639         
24640         
24641         if(this.boxLabel){
24642             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24643         //    viewEl.on('click', this.onClick,  this); 
24644         }
24645          if(this.checked){
24646             this.el.dom.checked =   'checked' ;
24647         }
24648          
24649     } 
24650     
24651     
24652 });//<script type="text/javascript">
24653
24654 /*
24655  * Based  Ext JS Library 1.1.1
24656  * Copyright(c) 2006-2007, Ext JS, LLC.
24657  * LGPL
24658  *
24659  */
24660  
24661 /**
24662  * @class Roo.HtmlEditorCore
24663  * @extends Roo.Component
24664  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24665  *
24666  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24667  */
24668
24669 Roo.HtmlEditorCore = function(config){
24670     
24671     
24672     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24673     
24674     
24675     this.addEvents({
24676         /**
24677          * @event initialize
24678          * Fires when the editor is fully initialized (including the iframe)
24679          * @param {Roo.HtmlEditorCore} this
24680          */
24681         initialize: true,
24682         /**
24683          * @event activate
24684          * Fires when the editor is first receives the focus. Any insertion must wait
24685          * until after this event.
24686          * @param {Roo.HtmlEditorCore} this
24687          */
24688         activate: true,
24689          /**
24690          * @event beforesync
24691          * Fires before the textarea is updated with content from the editor iframe. Return false
24692          * to cancel the sync.
24693          * @param {Roo.HtmlEditorCore} this
24694          * @param {String} html
24695          */
24696         beforesync: true,
24697          /**
24698          * @event beforepush
24699          * Fires before the iframe editor is updated with content from the textarea. Return false
24700          * to cancel the push.
24701          * @param {Roo.HtmlEditorCore} this
24702          * @param {String} html
24703          */
24704         beforepush: true,
24705          /**
24706          * @event sync
24707          * Fires when the textarea is updated with content from the editor iframe.
24708          * @param {Roo.HtmlEditorCore} this
24709          * @param {String} html
24710          */
24711         sync: true,
24712          /**
24713          * @event push
24714          * Fires when the iframe editor is updated with content from the textarea.
24715          * @param {Roo.HtmlEditorCore} this
24716          * @param {String} html
24717          */
24718         push: true,
24719         
24720         /**
24721          * @event editorevent
24722          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24723          * @param {Roo.HtmlEditorCore} this
24724          */
24725         editorevent: true
24726         
24727     });
24728     
24729     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24730     
24731     // defaults : white / black...
24732     this.applyBlacklists();
24733     
24734     
24735     
24736 };
24737
24738
24739 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24740
24741
24742      /**
24743      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24744      */
24745     
24746     owner : false,
24747     
24748      /**
24749      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24750      *                        Roo.resizable.
24751      */
24752     resizable : false,
24753      /**
24754      * @cfg {Number} height (in pixels)
24755      */   
24756     height: 300,
24757    /**
24758      * @cfg {Number} width (in pixels)
24759      */   
24760     width: 500,
24761     
24762     /**
24763      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24764      * 
24765      */
24766     stylesheets: false,
24767     
24768     // id of frame..
24769     frameId: false,
24770     
24771     // private properties
24772     validationEvent : false,
24773     deferHeight: true,
24774     initialized : false,
24775     activated : false,
24776     sourceEditMode : false,
24777     onFocus : Roo.emptyFn,
24778     iframePad:3,
24779     hideMode:'offsets',
24780     
24781     clearUp: true,
24782     
24783     // blacklist + whitelisted elements..
24784     black: false,
24785     white: false,
24786      
24787     
24788
24789     /**
24790      * Protected method that will not generally be called directly. It
24791      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24792      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24793      */
24794     getDocMarkup : function(){
24795         // body styles..
24796         var st = '';
24797         
24798         // inherit styels from page...?? 
24799         if (this.stylesheets === false) {
24800             
24801             Roo.get(document.head).select('style').each(function(node) {
24802                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24803             });
24804             
24805             Roo.get(document.head).select('link').each(function(node) { 
24806                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24807             });
24808             
24809         } else if (!this.stylesheets.length) {
24810                 // simple..
24811                 st = '<style type="text/css">' +
24812                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24813                    '</style>';
24814         } else { 
24815             
24816         }
24817         
24818         st +=  '<style type="text/css">' +
24819             'IMG { cursor: pointer } ' +
24820         '</style>';
24821
24822         
24823         return '<html><head>' + st  +
24824             //<style type="text/css">' +
24825             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24826             //'</style>' +
24827             ' </head><body class="roo-htmleditor-body"></body></html>';
24828     },
24829
24830     // private
24831     onRender : function(ct, position)
24832     {
24833         var _t = this;
24834         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24835         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24836         
24837         
24838         this.el.dom.style.border = '0 none';
24839         this.el.dom.setAttribute('tabIndex', -1);
24840         this.el.addClass('x-hidden hide');
24841         
24842         
24843         
24844         if(Roo.isIE){ // fix IE 1px bogus margin
24845             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24846         }
24847        
24848         
24849         this.frameId = Roo.id();
24850         
24851          
24852         
24853         var iframe = this.owner.wrap.createChild({
24854             tag: 'iframe',
24855             cls: 'form-control', // bootstrap..
24856             id: this.frameId,
24857             name: this.frameId,
24858             frameBorder : 'no',
24859             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24860         }, this.el
24861         );
24862         
24863         
24864         this.iframe = iframe.dom;
24865
24866          this.assignDocWin();
24867         
24868         this.doc.designMode = 'on';
24869        
24870         this.doc.open();
24871         this.doc.write(this.getDocMarkup());
24872         this.doc.close();
24873
24874         
24875         var task = { // must defer to wait for browser to be ready
24876             run : function(){
24877                 //console.log("run task?" + this.doc.readyState);
24878                 this.assignDocWin();
24879                 if(this.doc.body || this.doc.readyState == 'complete'){
24880                     try {
24881                         this.doc.designMode="on";
24882                     } catch (e) {
24883                         return;
24884                     }
24885                     Roo.TaskMgr.stop(task);
24886                     this.initEditor.defer(10, this);
24887                 }
24888             },
24889             interval : 10,
24890             duration: 10000,
24891             scope: this
24892         };
24893         Roo.TaskMgr.start(task);
24894
24895     },
24896
24897     // private
24898     onResize : function(w, h)
24899     {
24900          Roo.log('resize: ' +w + ',' + h );
24901         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24902         if(!this.iframe){
24903             return;
24904         }
24905         if(typeof w == 'number'){
24906             
24907             this.iframe.style.width = w + 'px';
24908         }
24909         if(typeof h == 'number'){
24910             
24911             this.iframe.style.height = h + 'px';
24912             if(this.doc){
24913                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24914             }
24915         }
24916         
24917     },
24918
24919     /**
24920      * Toggles the editor between standard and source edit mode.
24921      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24922      */
24923     toggleSourceEdit : function(sourceEditMode){
24924         
24925         this.sourceEditMode = sourceEditMode === true;
24926         
24927         if(this.sourceEditMode){
24928  
24929             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24930             
24931         }else{
24932             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24933             //this.iframe.className = '';
24934             this.deferFocus();
24935         }
24936         //this.setSize(this.owner.wrap.getSize());
24937         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24938     },
24939
24940     
24941   
24942
24943     /**
24944      * Protected method that will not generally be called directly. If you need/want
24945      * custom HTML cleanup, this is the method you should override.
24946      * @param {String} html The HTML to be cleaned
24947      * return {String} The cleaned HTML
24948      */
24949     cleanHtml : function(html){
24950         html = String(html);
24951         if(html.length > 5){
24952             if(Roo.isSafari){ // strip safari nonsense
24953                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24954             }
24955         }
24956         if(html == '&nbsp;'){
24957             html = '';
24958         }
24959         return html;
24960     },
24961
24962     /**
24963      * HTML Editor -> Textarea
24964      * Protected method that will not generally be called directly. Syncs the contents
24965      * of the editor iframe with the textarea.
24966      */
24967     syncValue : function(){
24968         if(this.initialized){
24969             var bd = (this.doc.body || this.doc.documentElement);
24970             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24971             var html = bd.innerHTML;
24972             if(Roo.isSafari){
24973                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24974                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24975                 if(m && m[1]){
24976                     html = '<div style="'+m[0]+'">' + html + '</div>';
24977                 }
24978             }
24979             html = this.cleanHtml(html);
24980             // fix up the special chars.. normaly like back quotes in word...
24981             // however we do not want to do this with chinese..
24982             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24983                 var cc = b.charCodeAt();
24984                 if (
24985                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24986                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24987                     (cc >= 0xf900 && cc < 0xfb00 )
24988                 ) {
24989                         return b;
24990                 }
24991                 return "&#"+cc+";" 
24992             });
24993             if(this.owner.fireEvent('beforesync', this, html) !== false){
24994                 this.el.dom.value = html;
24995                 this.owner.fireEvent('sync', this, html);
24996             }
24997         }
24998     },
24999
25000     /**
25001      * Protected method that will not generally be called directly. Pushes the value of the textarea
25002      * into the iframe editor.
25003      */
25004     pushValue : function(){
25005         if(this.initialized){
25006             var v = this.el.dom.value.trim();
25007             
25008 //            if(v.length < 1){
25009 //                v = '&#160;';
25010 //            }
25011             
25012             if(this.owner.fireEvent('beforepush', this, v) !== false){
25013                 var d = (this.doc.body || this.doc.documentElement);
25014                 d.innerHTML = v;
25015                 this.cleanUpPaste();
25016                 this.el.dom.value = d.innerHTML;
25017                 this.owner.fireEvent('push', this, v);
25018             }
25019         }
25020     },
25021
25022     // private
25023     deferFocus : function(){
25024         this.focus.defer(10, this);
25025     },
25026
25027     // doc'ed in Field
25028     focus : function(){
25029         if(this.win && !this.sourceEditMode){
25030             this.win.focus();
25031         }else{
25032             this.el.focus();
25033         }
25034     },
25035     
25036     assignDocWin: function()
25037     {
25038         var iframe = this.iframe;
25039         
25040          if(Roo.isIE){
25041             this.doc = iframe.contentWindow.document;
25042             this.win = iframe.contentWindow;
25043         } else {
25044 //            if (!Roo.get(this.frameId)) {
25045 //                return;
25046 //            }
25047 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25048 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25049             
25050             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25051                 return;
25052             }
25053             
25054             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25055             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25056         }
25057     },
25058     
25059     // private
25060     initEditor : function(){
25061         //console.log("INIT EDITOR");
25062         this.assignDocWin();
25063         
25064         
25065         
25066         this.doc.designMode="on";
25067         this.doc.open();
25068         this.doc.write(this.getDocMarkup());
25069         this.doc.close();
25070         
25071         var dbody = (this.doc.body || this.doc.documentElement);
25072         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25073         // this copies styles from the containing element into thsi one..
25074         // not sure why we need all of this..
25075         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25076         
25077         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25078         //ss['background-attachment'] = 'fixed'; // w3c
25079         dbody.bgProperties = 'fixed'; // ie
25080         //Roo.DomHelper.applyStyles(dbody, ss);
25081         Roo.EventManager.on(this.doc, {
25082             //'mousedown': this.onEditorEvent,
25083             'mouseup': this.onEditorEvent,
25084             'dblclick': this.onEditorEvent,
25085             'click': this.onEditorEvent,
25086             'keyup': this.onEditorEvent,
25087             buffer:100,
25088             scope: this
25089         });
25090         if(Roo.isGecko){
25091             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25092         }
25093         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25094             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25095         }
25096         this.initialized = true;
25097
25098         this.owner.fireEvent('initialize', this);
25099         this.pushValue();
25100     },
25101
25102     // private
25103     onDestroy : function(){
25104         
25105         
25106         
25107         if(this.rendered){
25108             
25109             //for (var i =0; i < this.toolbars.length;i++) {
25110             //    // fixme - ask toolbars for heights?
25111             //    this.toolbars[i].onDestroy();
25112            // }
25113             
25114             //this.wrap.dom.innerHTML = '';
25115             //this.wrap.remove();
25116         }
25117     },
25118
25119     // private
25120     onFirstFocus : function(){
25121         
25122         this.assignDocWin();
25123         
25124         
25125         this.activated = true;
25126          
25127     
25128         if(Roo.isGecko){ // prevent silly gecko errors
25129             this.win.focus();
25130             var s = this.win.getSelection();
25131             if(!s.focusNode || s.focusNode.nodeType != 3){
25132                 var r = s.getRangeAt(0);
25133                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25134                 r.collapse(true);
25135                 this.deferFocus();
25136             }
25137             try{
25138                 this.execCmd('useCSS', true);
25139                 this.execCmd('styleWithCSS', false);
25140             }catch(e){}
25141         }
25142         this.owner.fireEvent('activate', this);
25143     },
25144
25145     // private
25146     adjustFont: function(btn){
25147         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25148         //if(Roo.isSafari){ // safari
25149         //    adjust *= 2;
25150        // }
25151         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25152         if(Roo.isSafari){ // safari
25153             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25154             v =  (v < 10) ? 10 : v;
25155             v =  (v > 48) ? 48 : v;
25156             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25157             
25158         }
25159         
25160         
25161         v = Math.max(1, v+adjust);
25162         
25163         this.execCmd('FontSize', v  );
25164     },
25165
25166     onEditorEvent : function(e)
25167     {
25168         this.owner.fireEvent('editorevent', this, e);
25169       //  this.updateToolbar();
25170         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25171     },
25172
25173     insertTag : function(tg)
25174     {
25175         // could be a bit smarter... -> wrap the current selected tRoo..
25176         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25177             
25178             range = this.createRange(this.getSelection());
25179             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25180             wrappingNode.appendChild(range.extractContents());
25181             range.insertNode(wrappingNode);
25182
25183             return;
25184             
25185             
25186             
25187         }
25188         this.execCmd("formatblock",   tg);
25189         
25190     },
25191     
25192     insertText : function(txt)
25193     {
25194         
25195         
25196         var range = this.createRange();
25197         range.deleteContents();
25198                //alert(Sender.getAttribute('label'));
25199                
25200         range.insertNode(this.doc.createTextNode(txt));
25201     } ,
25202     
25203      
25204
25205     /**
25206      * Executes a Midas editor command on the editor document and performs necessary focus and
25207      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25208      * @param {String} cmd The Midas command
25209      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25210      */
25211     relayCmd : function(cmd, value){
25212         this.win.focus();
25213         this.execCmd(cmd, value);
25214         this.owner.fireEvent('editorevent', this);
25215         //this.updateToolbar();
25216         this.owner.deferFocus();
25217     },
25218
25219     /**
25220      * Executes a Midas editor command directly on the editor document.
25221      * For visual commands, you should use {@link #relayCmd} instead.
25222      * <b>This should only be called after the editor is initialized.</b>
25223      * @param {String} cmd The Midas command
25224      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25225      */
25226     execCmd : function(cmd, value){
25227         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25228         this.syncValue();
25229     },
25230  
25231  
25232    
25233     /**
25234      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25235      * to insert tRoo.
25236      * @param {String} text | dom node.. 
25237      */
25238     insertAtCursor : function(text)
25239     {
25240         
25241         
25242         
25243         if(!this.activated){
25244             return;
25245         }
25246         /*
25247         if(Roo.isIE){
25248             this.win.focus();
25249             var r = this.doc.selection.createRange();
25250             if(r){
25251                 r.collapse(true);
25252                 r.pasteHTML(text);
25253                 this.syncValue();
25254                 this.deferFocus();
25255             
25256             }
25257             return;
25258         }
25259         */
25260         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25261             this.win.focus();
25262             
25263             
25264             // from jquery ui (MIT licenced)
25265             var range, node;
25266             var win = this.win;
25267             
25268             if (win.getSelection && win.getSelection().getRangeAt) {
25269                 range = win.getSelection().getRangeAt(0);
25270                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25271                 range.insertNode(node);
25272             } else if (win.document.selection && win.document.selection.createRange) {
25273                 // no firefox support
25274                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25275                 win.document.selection.createRange().pasteHTML(txt);
25276             } else {
25277                 // no firefox support
25278                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25279                 this.execCmd('InsertHTML', txt);
25280             } 
25281             
25282             this.syncValue();
25283             
25284             this.deferFocus();
25285         }
25286     },
25287  // private
25288     mozKeyPress : function(e){
25289         if(e.ctrlKey){
25290             var c = e.getCharCode(), cmd;
25291           
25292             if(c > 0){
25293                 c = String.fromCharCode(c).toLowerCase();
25294                 switch(c){
25295                     case 'b':
25296                         cmd = 'bold';
25297                         break;
25298                     case 'i':
25299                         cmd = 'italic';
25300                         break;
25301                     
25302                     case 'u':
25303                         cmd = 'underline';
25304                         break;
25305                     
25306                     case 'v':
25307                         this.cleanUpPaste.defer(100, this);
25308                         return;
25309                         
25310                 }
25311                 if(cmd){
25312                     this.win.focus();
25313                     this.execCmd(cmd);
25314                     this.deferFocus();
25315                     e.preventDefault();
25316                 }
25317                 
25318             }
25319         }
25320     },
25321
25322     // private
25323     fixKeys : function(){ // load time branching for fastest keydown performance
25324         if(Roo.isIE){
25325             return function(e){
25326                 var k = e.getKey(), r;
25327                 if(k == e.TAB){
25328                     e.stopEvent();
25329                     r = this.doc.selection.createRange();
25330                     if(r){
25331                         r.collapse(true);
25332                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25333                         this.deferFocus();
25334                     }
25335                     return;
25336                 }
25337                 
25338                 if(k == e.ENTER){
25339                     r = this.doc.selection.createRange();
25340                     if(r){
25341                         var target = r.parentElement();
25342                         if(!target || target.tagName.toLowerCase() != 'li'){
25343                             e.stopEvent();
25344                             r.pasteHTML('<br />');
25345                             r.collapse(false);
25346                             r.select();
25347                         }
25348                     }
25349                 }
25350                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25351                     this.cleanUpPaste.defer(100, this);
25352                     return;
25353                 }
25354                 
25355                 
25356             };
25357         }else if(Roo.isOpera){
25358             return function(e){
25359                 var k = e.getKey();
25360                 if(k == e.TAB){
25361                     e.stopEvent();
25362                     this.win.focus();
25363                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25364                     this.deferFocus();
25365                 }
25366                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25367                     this.cleanUpPaste.defer(100, this);
25368                     return;
25369                 }
25370                 
25371             };
25372         }else if(Roo.isSafari){
25373             return function(e){
25374                 var k = e.getKey();
25375                 
25376                 if(k == e.TAB){
25377                     e.stopEvent();
25378                     this.execCmd('InsertText','\t');
25379                     this.deferFocus();
25380                     return;
25381                 }
25382                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25383                     this.cleanUpPaste.defer(100, this);
25384                     return;
25385                 }
25386                 
25387              };
25388         }
25389     }(),
25390     
25391     getAllAncestors: function()
25392     {
25393         var p = this.getSelectedNode();
25394         var a = [];
25395         if (!p) {
25396             a.push(p); // push blank onto stack..
25397             p = this.getParentElement();
25398         }
25399         
25400         
25401         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25402             a.push(p);
25403             p = p.parentNode;
25404         }
25405         a.push(this.doc.body);
25406         return a;
25407     },
25408     lastSel : false,
25409     lastSelNode : false,
25410     
25411     
25412     getSelection : function() 
25413     {
25414         this.assignDocWin();
25415         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25416     },
25417     
25418     getSelectedNode: function() 
25419     {
25420         // this may only work on Gecko!!!
25421         
25422         // should we cache this!!!!
25423         
25424         
25425         
25426          
25427         var range = this.createRange(this.getSelection()).cloneRange();
25428         
25429         if (Roo.isIE) {
25430             var parent = range.parentElement();
25431             while (true) {
25432                 var testRange = range.duplicate();
25433                 testRange.moveToElementText(parent);
25434                 if (testRange.inRange(range)) {
25435                     break;
25436                 }
25437                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25438                     break;
25439                 }
25440                 parent = parent.parentElement;
25441             }
25442             return parent;
25443         }
25444         
25445         // is ancestor a text element.
25446         var ac =  range.commonAncestorContainer;
25447         if (ac.nodeType == 3) {
25448             ac = ac.parentNode;
25449         }
25450         
25451         var ar = ac.childNodes;
25452          
25453         var nodes = [];
25454         var other_nodes = [];
25455         var has_other_nodes = false;
25456         for (var i=0;i<ar.length;i++) {
25457             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25458                 continue;
25459             }
25460             // fullly contained node.
25461             
25462             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25463                 nodes.push(ar[i]);
25464                 continue;
25465             }
25466             
25467             // probably selected..
25468             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25469                 other_nodes.push(ar[i]);
25470                 continue;
25471             }
25472             // outer..
25473             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25474                 continue;
25475             }
25476             
25477             
25478             has_other_nodes = true;
25479         }
25480         if (!nodes.length && other_nodes.length) {
25481             nodes= other_nodes;
25482         }
25483         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25484             return false;
25485         }
25486         
25487         return nodes[0];
25488     },
25489     createRange: function(sel)
25490     {
25491         // this has strange effects when using with 
25492         // top toolbar - not sure if it's a great idea.
25493         //this.editor.contentWindow.focus();
25494         if (typeof sel != "undefined") {
25495             try {
25496                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25497             } catch(e) {
25498                 return this.doc.createRange();
25499             }
25500         } else {
25501             return this.doc.createRange();
25502         }
25503     },
25504     getParentElement: function()
25505     {
25506         
25507         this.assignDocWin();
25508         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25509         
25510         var range = this.createRange(sel);
25511          
25512         try {
25513             var p = range.commonAncestorContainer;
25514             while (p.nodeType == 3) { // text node
25515                 p = p.parentNode;
25516             }
25517             return p;
25518         } catch (e) {
25519             return null;
25520         }
25521     
25522     },
25523     /***
25524      *
25525      * Range intersection.. the hard stuff...
25526      *  '-1' = before
25527      *  '0' = hits..
25528      *  '1' = after.
25529      *         [ -- selected range --- ]
25530      *   [fail]                        [fail]
25531      *
25532      *    basically..
25533      *      if end is before start or  hits it. fail.
25534      *      if start is after end or hits it fail.
25535      *
25536      *   if either hits (but other is outside. - then it's not 
25537      *   
25538      *    
25539      **/
25540     
25541     
25542     // @see http://www.thismuchiknow.co.uk/?p=64.
25543     rangeIntersectsNode : function(range, node)
25544     {
25545         var nodeRange = node.ownerDocument.createRange();
25546         try {
25547             nodeRange.selectNode(node);
25548         } catch (e) {
25549             nodeRange.selectNodeContents(node);
25550         }
25551     
25552         var rangeStartRange = range.cloneRange();
25553         rangeStartRange.collapse(true);
25554     
25555         var rangeEndRange = range.cloneRange();
25556         rangeEndRange.collapse(false);
25557     
25558         var nodeStartRange = nodeRange.cloneRange();
25559         nodeStartRange.collapse(true);
25560     
25561         var nodeEndRange = nodeRange.cloneRange();
25562         nodeEndRange.collapse(false);
25563     
25564         return rangeStartRange.compareBoundaryPoints(
25565                  Range.START_TO_START, nodeEndRange) == -1 &&
25566                rangeEndRange.compareBoundaryPoints(
25567                  Range.START_TO_START, nodeStartRange) == 1;
25568         
25569          
25570     },
25571     rangeCompareNode : function(range, node)
25572     {
25573         var nodeRange = node.ownerDocument.createRange();
25574         try {
25575             nodeRange.selectNode(node);
25576         } catch (e) {
25577             nodeRange.selectNodeContents(node);
25578         }
25579         
25580         
25581         range.collapse(true);
25582     
25583         nodeRange.collapse(true);
25584      
25585         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25586         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25587          
25588         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25589         
25590         var nodeIsBefore   =  ss == 1;
25591         var nodeIsAfter    = ee == -1;
25592         
25593         if (nodeIsBefore && nodeIsAfter)
25594             return 0; // outer
25595         if (!nodeIsBefore && nodeIsAfter)
25596             return 1; //right trailed.
25597         
25598         if (nodeIsBefore && !nodeIsAfter)
25599             return 2;  // left trailed.
25600         // fully contined.
25601         return 3;
25602     },
25603
25604     // private? - in a new class?
25605     cleanUpPaste :  function()
25606     {
25607         // cleans up the whole document..
25608         Roo.log('cleanuppaste');
25609         
25610         this.cleanUpChildren(this.doc.body);
25611         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25612         if (clean != this.doc.body.innerHTML) {
25613             this.doc.body.innerHTML = clean;
25614         }
25615         
25616     },
25617     
25618     cleanWordChars : function(input) {// change the chars to hex code
25619         var he = Roo.HtmlEditorCore;
25620         
25621         var output = input;
25622         Roo.each(he.swapCodes, function(sw) { 
25623             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25624             
25625             output = output.replace(swapper, sw[1]);
25626         });
25627         
25628         return output;
25629     },
25630     
25631     
25632     cleanUpChildren : function (n)
25633     {
25634         if (!n.childNodes.length) {
25635             return;
25636         }
25637         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25638            this.cleanUpChild(n.childNodes[i]);
25639         }
25640     },
25641     
25642     
25643         
25644     
25645     cleanUpChild : function (node)
25646     {
25647         var ed = this;
25648         //console.log(node);
25649         if (node.nodeName == "#text") {
25650             // clean up silly Windows -- stuff?
25651             return; 
25652         }
25653         if (node.nodeName == "#comment") {
25654             node.parentNode.removeChild(node);
25655             // clean up silly Windows -- stuff?
25656             return; 
25657         }
25658         var lcname = node.tagName.toLowerCase();
25659         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25660         // whitelist of tags..
25661         
25662         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25663             // remove node.
25664             node.parentNode.removeChild(node);
25665             return;
25666             
25667         }
25668         
25669         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25670         
25671         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25672         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25673         
25674         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25675         //    remove_keep_children = true;
25676         //}
25677         
25678         if (remove_keep_children) {
25679             this.cleanUpChildren(node);
25680             // inserts everything just before this node...
25681             while (node.childNodes.length) {
25682                 var cn = node.childNodes[0];
25683                 node.removeChild(cn);
25684                 node.parentNode.insertBefore(cn, node);
25685             }
25686             node.parentNode.removeChild(node);
25687             return;
25688         }
25689         
25690         if (!node.attributes || !node.attributes.length) {
25691             this.cleanUpChildren(node);
25692             return;
25693         }
25694         
25695         function cleanAttr(n,v)
25696         {
25697             
25698             if (v.match(/^\./) || v.match(/^\//)) {
25699                 return;
25700             }
25701             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25702                 return;
25703             }
25704             if (v.match(/^#/)) {
25705                 return;
25706             }
25707 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25708             node.removeAttribute(n);
25709             
25710         }
25711         
25712         var cwhite = this.cwhite;
25713         var cblack = this.cblack;
25714             
25715         function cleanStyle(n,v)
25716         {
25717             if (v.match(/expression/)) { //XSS?? should we even bother..
25718                 node.removeAttribute(n);
25719                 return;
25720             }
25721             
25722             var parts = v.split(/;/);
25723             var clean = [];
25724             
25725             Roo.each(parts, function(p) {
25726                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25727                 if (!p.length) {
25728                     return true;
25729                 }
25730                 var l = p.split(':').shift().replace(/\s+/g,'');
25731                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25732                 
25733                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25734 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25735                     //node.removeAttribute(n);
25736                     return true;
25737                 }
25738                 //Roo.log()
25739                 // only allow 'c whitelisted system attributes'
25740                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25741 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25742                     //node.removeAttribute(n);
25743                     return true;
25744                 }
25745                 
25746                 
25747                  
25748                 
25749                 clean.push(p);
25750                 return true;
25751             });
25752             if (clean.length) { 
25753                 node.setAttribute(n, clean.join(';'));
25754             } else {
25755                 node.removeAttribute(n);
25756             }
25757             
25758         }
25759         
25760         
25761         for (var i = node.attributes.length-1; i > -1 ; i--) {
25762             var a = node.attributes[i];
25763             //console.log(a);
25764             
25765             if (a.name.toLowerCase().substr(0,2)=='on')  {
25766                 node.removeAttribute(a.name);
25767                 continue;
25768             }
25769             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25770                 node.removeAttribute(a.name);
25771                 continue;
25772             }
25773             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25774                 cleanAttr(a.name,a.value); // fixme..
25775                 continue;
25776             }
25777             if (a.name == 'style') {
25778                 cleanStyle(a.name,a.value);
25779                 continue;
25780             }
25781             /// clean up MS crap..
25782             // tecnically this should be a list of valid class'es..
25783             
25784             
25785             if (a.name == 'class') {
25786                 if (a.value.match(/^Mso/)) {
25787                     node.className = '';
25788                 }
25789                 
25790                 if (a.value.match(/body/)) {
25791                     node.className = '';
25792                 }
25793                 continue;
25794             }
25795             
25796             // style cleanup!?
25797             // class cleanup?
25798             
25799         }
25800         
25801         
25802         this.cleanUpChildren(node);
25803         
25804         
25805     },
25806     
25807     /**
25808      * Clean up MS wordisms...
25809      */
25810     cleanWord : function(node)
25811     {
25812         
25813         
25814         if (!node) {
25815             this.cleanWord(this.doc.body);
25816             return;
25817         }
25818         if (node.nodeName == "#text") {
25819             // clean up silly Windows -- stuff?
25820             return; 
25821         }
25822         if (node.nodeName == "#comment") {
25823             node.parentNode.removeChild(node);
25824             // clean up silly Windows -- stuff?
25825             return; 
25826         }
25827         
25828         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25829             node.parentNode.removeChild(node);
25830             return;
25831         }
25832         
25833         // remove - but keep children..
25834         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25835             while (node.childNodes.length) {
25836                 var cn = node.childNodes[0];
25837                 node.removeChild(cn);
25838                 node.parentNode.insertBefore(cn, node);
25839             }
25840             node.parentNode.removeChild(node);
25841             this.iterateChildren(node, this.cleanWord);
25842             return;
25843         }
25844         // clean styles
25845         if (node.className.length) {
25846             
25847             var cn = node.className.split(/\W+/);
25848             var cna = [];
25849             Roo.each(cn, function(cls) {
25850                 if (cls.match(/Mso[a-zA-Z]+/)) {
25851                     return;
25852                 }
25853                 cna.push(cls);
25854             });
25855             node.className = cna.length ? cna.join(' ') : '';
25856             if (!cna.length) {
25857                 node.removeAttribute("class");
25858             }
25859         }
25860         
25861         if (node.hasAttribute("lang")) {
25862             node.removeAttribute("lang");
25863         }
25864         
25865         if (node.hasAttribute("style")) {
25866             
25867             var styles = node.getAttribute("style").split(";");
25868             var nstyle = [];
25869             Roo.each(styles, function(s) {
25870                 if (!s.match(/:/)) {
25871                     return;
25872                 }
25873                 var kv = s.split(":");
25874                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25875                     return;
25876                 }
25877                 // what ever is left... we allow.
25878                 nstyle.push(s);
25879             });
25880             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25881             if (!nstyle.length) {
25882                 node.removeAttribute('style');
25883             }
25884         }
25885         this.iterateChildren(node, this.cleanWord);
25886         
25887         
25888         
25889     },
25890     /**
25891      * iterateChildren of a Node, calling fn each time, using this as the scole..
25892      * @param {DomNode} node node to iterate children of.
25893      * @param {Function} fn method of this class to call on each item.
25894      */
25895     iterateChildren : function(node, fn)
25896     {
25897         if (!node.childNodes.length) {
25898                 return;
25899         }
25900         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25901            fn.call(this, node.childNodes[i])
25902         }
25903     },
25904     
25905     
25906     /**
25907      * cleanTableWidths.
25908      *
25909      * Quite often pasting from word etc.. results in tables with column and widths.
25910      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25911      *
25912      */
25913     cleanTableWidths : function(node)
25914     {
25915          
25916          
25917         if (!node) {
25918             this.cleanTableWidths(this.doc.body);
25919             return;
25920         }
25921         
25922         // ignore list...
25923         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25924             return; 
25925         }
25926         Roo.log(node.tagName);
25927         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25928             this.iterateChildren(node, this.cleanTableWidths);
25929             return;
25930         }
25931         if (node.hasAttribute('width')) {
25932             node.removeAttribute('width');
25933         }
25934         
25935          
25936         if (node.hasAttribute("style")) {
25937             // pretty basic...
25938             
25939             var styles = node.getAttribute("style").split(";");
25940             var nstyle = [];
25941             Roo.each(styles, function(s) {
25942                 if (!s.match(/:/)) {
25943                     return;
25944                 }
25945                 var kv = s.split(":");
25946                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25947                     return;
25948                 }
25949                 // what ever is left... we allow.
25950                 nstyle.push(s);
25951             });
25952             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25953             if (!nstyle.length) {
25954                 node.removeAttribute('style');
25955             }
25956         }
25957         
25958         this.iterateChildren(node, this.cleanTableWidths);
25959         
25960         
25961     },
25962     
25963     
25964     
25965     
25966     domToHTML : function(currentElement, depth, nopadtext) {
25967         
25968         depth = depth || 0;
25969         nopadtext = nopadtext || false;
25970     
25971         if (!currentElement) {
25972             return this.domToHTML(this.doc.body);
25973         }
25974         
25975         //Roo.log(currentElement);
25976         var j;
25977         var allText = false;
25978         var nodeName = currentElement.nodeName;
25979         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25980         
25981         if  (nodeName == '#text') {
25982             
25983             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25984         }
25985         
25986         
25987         var ret = '';
25988         if (nodeName != 'BODY') {
25989              
25990             var i = 0;
25991             // Prints the node tagName, such as <A>, <IMG>, etc
25992             if (tagName) {
25993                 var attr = [];
25994                 for(i = 0; i < currentElement.attributes.length;i++) {
25995                     // quoting?
25996                     var aname = currentElement.attributes.item(i).name;
25997                     if (!currentElement.attributes.item(i).value.length) {
25998                         continue;
25999                     }
26000                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26001                 }
26002                 
26003                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26004             } 
26005             else {
26006                 
26007                 // eack
26008             }
26009         } else {
26010             tagName = false;
26011         }
26012         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26013             return ret;
26014         }
26015         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26016             nopadtext = true;
26017         }
26018         
26019         
26020         // Traverse the tree
26021         i = 0;
26022         var currentElementChild = currentElement.childNodes.item(i);
26023         var allText = true;
26024         var innerHTML  = '';
26025         lastnode = '';
26026         while (currentElementChild) {
26027             // Formatting code (indent the tree so it looks nice on the screen)
26028             var nopad = nopadtext;
26029             if (lastnode == 'SPAN') {
26030                 nopad  = true;
26031             }
26032             // text
26033             if  (currentElementChild.nodeName == '#text') {
26034                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26035                 toadd = nopadtext ? toadd : toadd.trim();
26036                 if (!nopad && toadd.length > 80) {
26037                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26038                 }
26039                 innerHTML  += toadd;
26040                 
26041                 i++;
26042                 currentElementChild = currentElement.childNodes.item(i);
26043                 lastNode = '';
26044                 continue;
26045             }
26046             allText = false;
26047             
26048             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26049                 
26050             // Recursively traverse the tree structure of the child node
26051             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26052             lastnode = currentElementChild.nodeName;
26053             i++;
26054             currentElementChild=currentElement.childNodes.item(i);
26055         }
26056         
26057         ret += innerHTML;
26058         
26059         if (!allText) {
26060                 // The remaining code is mostly for formatting the tree
26061             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26062         }
26063         
26064         
26065         if (tagName) {
26066             ret+= "</"+tagName+">";
26067         }
26068         return ret;
26069         
26070     },
26071         
26072     applyBlacklists : function()
26073     {
26074         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26075         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26076         
26077         this.white = [];
26078         this.black = [];
26079         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26080             if (b.indexOf(tag) > -1) {
26081                 return;
26082             }
26083             this.white.push(tag);
26084             
26085         }, this);
26086         
26087         Roo.each(w, function(tag) {
26088             if (b.indexOf(tag) > -1) {
26089                 return;
26090             }
26091             if (this.white.indexOf(tag) > -1) {
26092                 return;
26093             }
26094             this.white.push(tag);
26095             
26096         }, this);
26097         
26098         
26099         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26100             if (w.indexOf(tag) > -1) {
26101                 return;
26102             }
26103             this.black.push(tag);
26104             
26105         }, this);
26106         
26107         Roo.each(b, function(tag) {
26108             if (w.indexOf(tag) > -1) {
26109                 return;
26110             }
26111             if (this.black.indexOf(tag) > -1) {
26112                 return;
26113             }
26114             this.black.push(tag);
26115             
26116         }, this);
26117         
26118         
26119         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26120         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26121         
26122         this.cwhite = [];
26123         this.cblack = [];
26124         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26125             if (b.indexOf(tag) > -1) {
26126                 return;
26127             }
26128             this.cwhite.push(tag);
26129             
26130         }, this);
26131         
26132         Roo.each(w, function(tag) {
26133             if (b.indexOf(tag) > -1) {
26134                 return;
26135             }
26136             if (this.cwhite.indexOf(tag) > -1) {
26137                 return;
26138             }
26139             this.cwhite.push(tag);
26140             
26141         }, this);
26142         
26143         
26144         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26145             if (w.indexOf(tag) > -1) {
26146                 return;
26147             }
26148             this.cblack.push(tag);
26149             
26150         }, this);
26151         
26152         Roo.each(b, function(tag) {
26153             if (w.indexOf(tag) > -1) {
26154                 return;
26155             }
26156             if (this.cblack.indexOf(tag) > -1) {
26157                 return;
26158             }
26159             this.cblack.push(tag);
26160             
26161         }, this);
26162     },
26163     
26164     setStylesheets : function(stylesheets)
26165     {
26166         if(typeof(stylesheets) == 'string'){
26167             Roo.get(this.iframe.contentDocument.head).createChild({
26168                 tag : 'link',
26169                 rel : 'stylesheet',
26170                 type : 'text/css',
26171                 href : stylesheets
26172             });
26173             
26174             return;
26175         }
26176         var _this = this;
26177      
26178         Roo.each(stylesheets, function(s) {
26179             if(!s.length){
26180                 return;
26181             }
26182             
26183             Roo.get(_this.iframe.contentDocument.head).createChild({
26184                 tag : 'link',
26185                 rel : 'stylesheet',
26186                 type : 'text/css',
26187                 href : s
26188             });
26189         });
26190
26191         
26192     },
26193     
26194     removeStylesheets : function()
26195     {
26196         var _this = this;
26197         
26198         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26199             s.remove();
26200         });
26201     }
26202     
26203     // hide stuff that is not compatible
26204     /**
26205      * @event blur
26206      * @hide
26207      */
26208     /**
26209      * @event change
26210      * @hide
26211      */
26212     /**
26213      * @event focus
26214      * @hide
26215      */
26216     /**
26217      * @event specialkey
26218      * @hide
26219      */
26220     /**
26221      * @cfg {String} fieldClass @hide
26222      */
26223     /**
26224      * @cfg {String} focusClass @hide
26225      */
26226     /**
26227      * @cfg {String} autoCreate @hide
26228      */
26229     /**
26230      * @cfg {String} inputType @hide
26231      */
26232     /**
26233      * @cfg {String} invalidClass @hide
26234      */
26235     /**
26236      * @cfg {String} invalidText @hide
26237      */
26238     /**
26239      * @cfg {String} msgFx @hide
26240      */
26241     /**
26242      * @cfg {String} validateOnBlur @hide
26243      */
26244 });
26245
26246 Roo.HtmlEditorCore.white = [
26247         'area', 'br', 'img', 'input', 'hr', 'wbr',
26248         
26249        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26250        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26251        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26252        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26253        'table',   'ul',         'xmp', 
26254        
26255        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26256       'thead',   'tr', 
26257      
26258       'dir', 'menu', 'ol', 'ul', 'dl',
26259        
26260       'embed',  'object'
26261 ];
26262
26263
26264 Roo.HtmlEditorCore.black = [
26265     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26266         'applet', // 
26267         'base',   'basefont', 'bgsound', 'blink',  'body', 
26268         'frame',  'frameset', 'head',    'html',   'ilayer', 
26269         'iframe', 'layer',  'link',     'meta',    'object',   
26270         'script', 'style' ,'title',  'xml' // clean later..
26271 ];
26272 Roo.HtmlEditorCore.clean = [
26273     'script', 'style', 'title', 'xml'
26274 ];
26275 Roo.HtmlEditorCore.remove = [
26276     'font'
26277 ];
26278 // attributes..
26279
26280 Roo.HtmlEditorCore.ablack = [
26281     'on'
26282 ];
26283     
26284 Roo.HtmlEditorCore.aclean = [ 
26285     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26286 ];
26287
26288 // protocols..
26289 Roo.HtmlEditorCore.pwhite= [
26290         'http',  'https',  'mailto'
26291 ];
26292
26293 // white listed style attributes.
26294 Roo.HtmlEditorCore.cwhite= [
26295       //  'text-align', /// default is to allow most things..
26296       
26297          
26298 //        'font-size'//??
26299 ];
26300
26301 // black listed style attributes.
26302 Roo.HtmlEditorCore.cblack= [
26303       //  'font-size' -- this can be set by the project 
26304 ];
26305
26306
26307 Roo.HtmlEditorCore.swapCodes   =[ 
26308     [    8211, "--" ], 
26309     [    8212, "--" ], 
26310     [    8216,  "'" ],  
26311     [    8217, "'" ],  
26312     [    8220, '"' ],  
26313     [    8221, '"' ],  
26314     [    8226, "*" ],  
26315     [    8230, "..." ]
26316 ]; 
26317
26318     //<script type="text/javascript">
26319
26320 /*
26321  * Ext JS Library 1.1.1
26322  * Copyright(c) 2006-2007, Ext JS, LLC.
26323  * Licence LGPL
26324  * 
26325  */
26326  
26327  
26328 Roo.form.HtmlEditor = function(config){
26329     
26330     
26331     
26332     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26333     
26334     if (!this.toolbars) {
26335         this.toolbars = [];
26336     }
26337     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26338     
26339     
26340 };
26341
26342 /**
26343  * @class Roo.form.HtmlEditor
26344  * @extends Roo.form.Field
26345  * Provides a lightweight HTML Editor component.
26346  *
26347  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26348  * 
26349  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26350  * supported by this editor.</b><br/><br/>
26351  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26352  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26353  */
26354 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26355     /**
26356      * @cfg {Boolean} clearUp
26357      */
26358     clearUp : true,
26359       /**
26360      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26361      */
26362     toolbars : false,
26363    
26364      /**
26365      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26366      *                        Roo.resizable.
26367      */
26368     resizable : false,
26369      /**
26370      * @cfg {Number} height (in pixels)
26371      */   
26372     height: 300,
26373    /**
26374      * @cfg {Number} width (in pixels)
26375      */   
26376     width: 500,
26377     
26378     /**
26379      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26380      * 
26381      */
26382     stylesheets: false,
26383     
26384     
26385      /**
26386      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26387      * 
26388      */
26389     cblack: false,
26390     /**
26391      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26392      * 
26393      */
26394     cwhite: false,
26395     
26396      /**
26397      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26398      * 
26399      */
26400     black: false,
26401     /**
26402      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26403      * 
26404      */
26405     white: false,
26406     
26407     // id of frame..
26408     frameId: false,
26409     
26410     // private properties
26411     validationEvent : false,
26412     deferHeight: true,
26413     initialized : false,
26414     activated : false,
26415     
26416     onFocus : Roo.emptyFn,
26417     iframePad:3,
26418     hideMode:'offsets',
26419     
26420     actionMode : 'container', // defaults to hiding it...
26421     
26422     defaultAutoCreate : { // modified by initCompnoent..
26423         tag: "textarea",
26424         style:"width:500px;height:300px;",
26425         autocomplete: "new-password"
26426     },
26427
26428     // private
26429     initComponent : function(){
26430         this.addEvents({
26431             /**
26432              * @event initialize
26433              * Fires when the editor is fully initialized (including the iframe)
26434              * @param {HtmlEditor} this
26435              */
26436             initialize: true,
26437             /**
26438              * @event activate
26439              * Fires when the editor is first receives the focus. Any insertion must wait
26440              * until after this event.
26441              * @param {HtmlEditor} this
26442              */
26443             activate: true,
26444              /**
26445              * @event beforesync
26446              * Fires before the textarea is updated with content from the editor iframe. Return false
26447              * to cancel the sync.
26448              * @param {HtmlEditor} this
26449              * @param {String} html
26450              */
26451             beforesync: true,
26452              /**
26453              * @event beforepush
26454              * Fires before the iframe editor is updated with content from the textarea. Return false
26455              * to cancel the push.
26456              * @param {HtmlEditor} this
26457              * @param {String} html
26458              */
26459             beforepush: true,
26460              /**
26461              * @event sync
26462              * Fires when the textarea is updated with content from the editor iframe.
26463              * @param {HtmlEditor} this
26464              * @param {String} html
26465              */
26466             sync: true,
26467              /**
26468              * @event push
26469              * Fires when the iframe editor is updated with content from the textarea.
26470              * @param {HtmlEditor} this
26471              * @param {String} html
26472              */
26473             push: true,
26474              /**
26475              * @event editmodechange
26476              * Fires when the editor switches edit modes
26477              * @param {HtmlEditor} this
26478              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26479              */
26480             editmodechange: true,
26481             /**
26482              * @event editorevent
26483              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26484              * @param {HtmlEditor} this
26485              */
26486             editorevent: true,
26487             /**
26488              * @event firstfocus
26489              * Fires when on first focus - needed by toolbars..
26490              * @param {HtmlEditor} this
26491              */
26492             firstfocus: true,
26493             /**
26494              * @event autosave
26495              * Auto save the htmlEditor value as a file into Events
26496              * @param {HtmlEditor} this
26497              */
26498             autosave: true,
26499             /**
26500              * @event savedpreview
26501              * preview the saved version of htmlEditor
26502              * @param {HtmlEditor} this
26503              */
26504             savedpreview: true,
26505             
26506             /**
26507             * @event stylesheetsclick
26508             * Fires when press the Sytlesheets button
26509             * @param {Roo.HtmlEditorCore} this
26510             */
26511             stylesheetsclick: true
26512         });
26513         this.defaultAutoCreate =  {
26514             tag: "textarea",
26515             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26516             autocomplete: "new-password"
26517         };
26518     },
26519
26520     /**
26521      * Protected method that will not generally be called directly. It
26522      * is called when the editor creates its toolbar. Override this method if you need to
26523      * add custom toolbar buttons.
26524      * @param {HtmlEditor} editor
26525      */
26526     createToolbar : function(editor){
26527         Roo.log("create toolbars");
26528         if (!editor.toolbars || !editor.toolbars.length) {
26529             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26530         }
26531         
26532         for (var i =0 ; i < editor.toolbars.length;i++) {
26533             editor.toolbars[i] = Roo.factory(
26534                     typeof(editor.toolbars[i]) == 'string' ?
26535                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26536                 Roo.form.HtmlEditor);
26537             editor.toolbars[i].init(editor);
26538         }
26539          
26540         
26541     },
26542
26543      
26544     // private
26545     onRender : function(ct, position)
26546     {
26547         var _t = this;
26548         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26549         
26550         this.wrap = this.el.wrap({
26551             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26552         });
26553         
26554         this.editorcore.onRender(ct, position);
26555          
26556         if (this.resizable) {
26557             this.resizeEl = new Roo.Resizable(this.wrap, {
26558                 pinned : true,
26559                 wrap: true,
26560                 dynamic : true,
26561                 minHeight : this.height,
26562                 height: this.height,
26563                 handles : this.resizable,
26564                 width: this.width,
26565                 listeners : {
26566                     resize : function(r, w, h) {
26567                         _t.onResize(w,h); // -something
26568                     }
26569                 }
26570             });
26571             
26572         }
26573         this.createToolbar(this);
26574        
26575         
26576         if(!this.width){
26577             this.setSize(this.wrap.getSize());
26578         }
26579         if (this.resizeEl) {
26580             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26581             // should trigger onReize..
26582         }
26583         
26584         this.keyNav = new Roo.KeyNav(this.el, {
26585             
26586             "tab" : function(e){
26587                 e.preventDefault();
26588                 
26589                 var value = this.getValue();
26590                 
26591                 var start = this.el.dom.selectionStart;
26592                 var end = this.el.dom.selectionEnd;
26593                 
26594                 if(!e.shiftKey){
26595                     
26596                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26597                     this.el.dom.setSelectionRange(end + 1, end + 1);
26598                     return;
26599                 }
26600                 
26601                 var f = value.substring(0, start).split("\t");
26602                 
26603                 if(f.pop().length != 0){
26604                     return;
26605                 }
26606                 
26607                 this.setValue(f.join("\t") + value.substring(end));
26608                 this.el.dom.setSelectionRange(start - 1, start - 1);
26609                 
26610             },
26611             
26612             "home" : function(e){
26613                 e.preventDefault();
26614                 
26615                 var curr = this.el.dom.selectionStart;
26616                 var lines = this.getValue().split("\n");
26617                 
26618                 if(!lines.length){
26619                     return;
26620                 }
26621                 
26622                 if(e.ctrlKey){
26623                     this.el.dom.setSelectionRange(0, 0);
26624                     return;
26625                 }
26626                 
26627                 var pos = 0;
26628                 
26629                 for (var i = 0; i < lines.length;i++) {
26630                     pos += lines[i].length;
26631                     
26632                     if(i != 0){
26633                         pos += 1;
26634                     }
26635                     
26636                     if(pos < curr){
26637                         continue;
26638                     }
26639                     
26640                     pos -= lines[i].length;
26641                     
26642                     break;
26643                 }
26644                 
26645                 if(!e.shiftKey){
26646                     this.el.dom.setSelectionRange(pos, pos);
26647                     return;
26648                 }
26649                 
26650                 this.el.dom.selectionStart = pos;
26651                 this.el.dom.selectionEnd = curr;
26652             },
26653             
26654             "end" : function(e){
26655                 e.preventDefault();
26656                 
26657                 var curr = this.el.dom.selectionStart;
26658                 var lines = this.getValue().split("\n");
26659                 
26660                 if(!lines.length){
26661                     return;
26662                 }
26663                 
26664                 if(e.ctrlKey){
26665                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26666                     return;
26667                 }
26668                 
26669                 var pos = 0;
26670                 
26671                 for (var i = 0; i < lines.length;i++) {
26672                     
26673                     pos += lines[i].length;
26674                     
26675                     if(i != 0){
26676                         pos += 1;
26677                     }
26678                     
26679                     if(pos < curr){
26680                         continue;
26681                     }
26682                     
26683                     break;
26684                 }
26685                 
26686                 if(!e.shiftKey){
26687                     this.el.dom.setSelectionRange(pos, pos);
26688                     return;
26689                 }
26690                 
26691                 this.el.dom.selectionStart = curr;
26692                 this.el.dom.selectionEnd = pos;
26693             },
26694
26695             scope : this,
26696
26697             doRelay : function(foo, bar, hname){
26698                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26699             },
26700
26701             forceKeyDown: true
26702         });
26703         
26704 //        if(this.autosave && this.w){
26705 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26706 //        }
26707     },
26708
26709     // private
26710     onResize : function(w, h)
26711     {
26712         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26713         var ew = false;
26714         var eh = false;
26715         
26716         if(this.el ){
26717             if(typeof w == 'number'){
26718                 var aw = w - this.wrap.getFrameWidth('lr');
26719                 this.el.setWidth(this.adjustWidth('textarea', aw));
26720                 ew = aw;
26721             }
26722             if(typeof h == 'number'){
26723                 var tbh = 0;
26724                 for (var i =0; i < this.toolbars.length;i++) {
26725                     // fixme - ask toolbars for heights?
26726                     tbh += this.toolbars[i].tb.el.getHeight();
26727                     if (this.toolbars[i].footer) {
26728                         tbh += this.toolbars[i].footer.el.getHeight();
26729                     }
26730                 }
26731                 
26732                 
26733                 
26734                 
26735                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26736                 ah -= 5; // knock a few pixes off for look..
26737 //                Roo.log(ah);
26738                 this.el.setHeight(this.adjustWidth('textarea', ah));
26739                 var eh = ah;
26740             }
26741         }
26742         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26743         this.editorcore.onResize(ew,eh);
26744         
26745     },
26746
26747     /**
26748      * Toggles the editor between standard and source edit mode.
26749      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26750      */
26751     toggleSourceEdit : function(sourceEditMode)
26752     {
26753         this.editorcore.toggleSourceEdit(sourceEditMode);
26754         
26755         if(this.editorcore.sourceEditMode){
26756             Roo.log('editor - showing textarea');
26757             
26758 //            Roo.log('in');
26759 //            Roo.log(this.syncValue());
26760             this.editorcore.syncValue();
26761             this.el.removeClass('x-hidden');
26762             this.el.dom.removeAttribute('tabIndex');
26763             this.el.focus();
26764             
26765             for (var i = 0; i < this.toolbars.length; i++) {
26766                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26767                     this.toolbars[i].tb.hide();
26768                     this.toolbars[i].footer.hide();
26769                 }
26770             }
26771             
26772         }else{
26773             Roo.log('editor - hiding textarea');
26774 //            Roo.log('out')
26775 //            Roo.log(this.pushValue()); 
26776             this.editorcore.pushValue();
26777             
26778             this.el.addClass('x-hidden');
26779             this.el.dom.setAttribute('tabIndex', -1);
26780             
26781             for (var i = 0; i < this.toolbars.length; i++) {
26782                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26783                     this.toolbars[i].tb.show();
26784                     this.toolbars[i].footer.show();
26785                 }
26786             }
26787             
26788             //this.deferFocus();
26789         }
26790         
26791         this.setSize(this.wrap.getSize());
26792         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26793         
26794         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26795     },
26796  
26797     // private (for BoxComponent)
26798     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26799
26800     // private (for BoxComponent)
26801     getResizeEl : function(){
26802         return this.wrap;
26803     },
26804
26805     // private (for BoxComponent)
26806     getPositionEl : function(){
26807         return this.wrap;
26808     },
26809
26810     // private
26811     initEvents : function(){
26812         this.originalValue = this.getValue();
26813     },
26814
26815     /**
26816      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26817      * @method
26818      */
26819     markInvalid : Roo.emptyFn,
26820     /**
26821      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26822      * @method
26823      */
26824     clearInvalid : Roo.emptyFn,
26825
26826     setValue : function(v){
26827         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26828         this.editorcore.pushValue();
26829     },
26830
26831      
26832     // private
26833     deferFocus : function(){
26834         this.focus.defer(10, this);
26835     },
26836
26837     // doc'ed in Field
26838     focus : function(){
26839         this.editorcore.focus();
26840         
26841     },
26842       
26843
26844     // private
26845     onDestroy : function(){
26846         
26847         
26848         
26849         if(this.rendered){
26850             
26851             for (var i =0; i < this.toolbars.length;i++) {
26852                 // fixme - ask toolbars for heights?
26853                 this.toolbars[i].onDestroy();
26854             }
26855             
26856             this.wrap.dom.innerHTML = '';
26857             this.wrap.remove();
26858         }
26859     },
26860
26861     // private
26862     onFirstFocus : function(){
26863         //Roo.log("onFirstFocus");
26864         this.editorcore.onFirstFocus();
26865          for (var i =0; i < this.toolbars.length;i++) {
26866             this.toolbars[i].onFirstFocus();
26867         }
26868         
26869     },
26870     
26871     // private
26872     syncValue : function()
26873     {
26874         this.editorcore.syncValue();
26875     },
26876     
26877     pushValue : function()
26878     {
26879         this.editorcore.pushValue();
26880     },
26881     
26882     setStylesheets : function(stylesheets)
26883     {
26884         this.editorcore.setStylesheets(stylesheets);
26885     },
26886     
26887     removeStylesheets : function()
26888     {
26889         this.editorcore.removeStylesheets();
26890     }
26891      
26892     
26893     // hide stuff that is not compatible
26894     /**
26895      * @event blur
26896      * @hide
26897      */
26898     /**
26899      * @event change
26900      * @hide
26901      */
26902     /**
26903      * @event focus
26904      * @hide
26905      */
26906     /**
26907      * @event specialkey
26908      * @hide
26909      */
26910     /**
26911      * @cfg {String} fieldClass @hide
26912      */
26913     /**
26914      * @cfg {String} focusClass @hide
26915      */
26916     /**
26917      * @cfg {String} autoCreate @hide
26918      */
26919     /**
26920      * @cfg {String} inputType @hide
26921      */
26922     /**
26923      * @cfg {String} invalidClass @hide
26924      */
26925     /**
26926      * @cfg {String} invalidText @hide
26927      */
26928     /**
26929      * @cfg {String} msgFx @hide
26930      */
26931     /**
26932      * @cfg {String} validateOnBlur @hide
26933      */
26934 });
26935  
26936     // <script type="text/javascript">
26937 /*
26938  * Based on
26939  * Ext JS Library 1.1.1
26940  * Copyright(c) 2006-2007, Ext JS, LLC.
26941  *  
26942  
26943  */
26944
26945 /**
26946  * @class Roo.form.HtmlEditorToolbar1
26947  * Basic Toolbar
26948  * 
26949  * Usage:
26950  *
26951  new Roo.form.HtmlEditor({
26952     ....
26953     toolbars : [
26954         new Roo.form.HtmlEditorToolbar1({
26955             disable : { fonts: 1 , format: 1, ..., ... , ...],
26956             btns : [ .... ]
26957         })
26958     }
26959      
26960  * 
26961  * @cfg {Object} disable List of elements to disable..
26962  * @cfg {Array} btns List of additional buttons.
26963  * 
26964  * 
26965  * NEEDS Extra CSS? 
26966  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26967  */
26968  
26969 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26970 {
26971     
26972     Roo.apply(this, config);
26973     
26974     // default disabled, based on 'good practice'..
26975     this.disable = this.disable || {};
26976     Roo.applyIf(this.disable, {
26977         fontSize : true,
26978         colors : true,
26979         specialElements : true
26980     });
26981     
26982     
26983     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26984     // dont call parent... till later.
26985 }
26986
26987 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26988     
26989     tb: false,
26990     
26991     rendered: false,
26992     
26993     editor : false,
26994     editorcore : false,
26995     /**
26996      * @cfg {Object} disable  List of toolbar elements to disable
26997          
26998      */
26999     disable : false,
27000     
27001     
27002      /**
27003      * @cfg {String} createLinkText The default text for the create link prompt
27004      */
27005     createLinkText : 'Please enter the URL for the link:',
27006     /**
27007      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27008      */
27009     defaultLinkValue : 'http:/'+'/',
27010    
27011     
27012       /**
27013      * @cfg {Array} fontFamilies An array of available font families
27014      */
27015     fontFamilies : [
27016         'Arial',
27017         'Courier New',
27018         'Tahoma',
27019         'Times New Roman',
27020         'Verdana'
27021     ],
27022     
27023     specialChars : [
27024            "&#169;",
27025           "&#174;",     
27026           "&#8482;",    
27027           "&#163;" ,    
27028          // "&#8212;",    
27029           "&#8230;",    
27030           "&#247;" ,    
27031         //  "&#225;" ,     ?? a acute?
27032            "&#8364;"    , //Euro
27033        //   "&#8220;"    ,
27034         //  "&#8221;"    ,
27035         //  "&#8226;"    ,
27036           "&#176;"  //   , // degrees
27037
27038          // "&#233;"     , // e ecute
27039          // "&#250;"     , // u ecute?
27040     ],
27041     
27042     specialElements : [
27043         {
27044             text: "Insert Table",
27045             xtype: 'MenuItem',
27046             xns : Roo.Menu,
27047             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27048                 
27049         },
27050         {    
27051             text: "Insert Image",
27052             xtype: 'MenuItem',
27053             xns : Roo.Menu,
27054             ihtml : '<img src="about:blank"/>'
27055             
27056         }
27057         
27058          
27059     ],
27060     
27061     
27062     inputElements : [ 
27063             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27064             "input:submit", "input:button", "select", "textarea", "label" ],
27065     formats : [
27066         ["p"] ,  
27067         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27068         ["pre"],[ "code"], 
27069         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27070         ['div'],['span']
27071     ],
27072     
27073     cleanStyles : [
27074         "font-size"
27075     ],
27076      /**
27077      * @cfg {String} defaultFont default font to use.
27078      */
27079     defaultFont: 'tahoma',
27080    
27081     fontSelect : false,
27082     
27083     
27084     formatCombo : false,
27085     
27086     init : function(editor)
27087     {
27088         this.editor = editor;
27089         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27090         var editorcore = this.editorcore;
27091         
27092         var _t = this;
27093         
27094         var fid = editorcore.frameId;
27095         var etb = this;
27096         function btn(id, toggle, handler){
27097             var xid = fid + '-'+ id ;
27098             return {
27099                 id : xid,
27100                 cmd : id,
27101                 cls : 'x-btn-icon x-edit-'+id,
27102                 enableToggle:toggle !== false,
27103                 scope: _t, // was editor...
27104                 handler:handler||_t.relayBtnCmd,
27105                 clickEvent:'mousedown',
27106                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27107                 tabIndex:-1
27108             };
27109         }
27110         
27111         
27112         
27113         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27114         this.tb = tb;
27115          // stop form submits
27116         tb.el.on('click', function(e){
27117             e.preventDefault(); // what does this do?
27118         });
27119
27120         if(!this.disable.font) { // && !Roo.isSafari){
27121             /* why no safari for fonts 
27122             editor.fontSelect = tb.el.createChild({
27123                 tag:'select',
27124                 tabIndex: -1,
27125                 cls:'x-font-select',
27126                 html: this.createFontOptions()
27127             });
27128             
27129             editor.fontSelect.on('change', function(){
27130                 var font = editor.fontSelect.dom.value;
27131                 editor.relayCmd('fontname', font);
27132                 editor.deferFocus();
27133             }, editor);
27134             
27135             tb.add(
27136                 editor.fontSelect.dom,
27137                 '-'
27138             );
27139             */
27140             
27141         };
27142         if(!this.disable.formats){
27143             this.formatCombo = new Roo.form.ComboBox({
27144                 store: new Roo.data.SimpleStore({
27145                     id : 'tag',
27146                     fields: ['tag'],
27147                     data : this.formats // from states.js
27148                 }),
27149                 blockFocus : true,
27150                 name : '',
27151                 //autoCreate : {tag: "div",  size: "20"},
27152                 displayField:'tag',
27153                 typeAhead: false,
27154                 mode: 'local',
27155                 editable : false,
27156                 triggerAction: 'all',
27157                 emptyText:'Add tag',
27158                 selectOnFocus:true,
27159                 width:135,
27160                 listeners : {
27161                     'select': function(c, r, i) {
27162                         editorcore.insertTag(r.get('tag'));
27163                         editor.focus();
27164                     }
27165                 }
27166
27167             });
27168             tb.addField(this.formatCombo);
27169             
27170         }
27171         
27172         if(!this.disable.format){
27173             tb.add(
27174                 btn('bold'),
27175                 btn('italic'),
27176                 btn('underline')
27177             );
27178         };
27179         if(!this.disable.fontSize){
27180             tb.add(
27181                 '-',
27182                 
27183                 
27184                 btn('increasefontsize', false, editorcore.adjustFont),
27185                 btn('decreasefontsize', false, editorcore.adjustFont)
27186             );
27187         };
27188         
27189         
27190         if(!this.disable.colors){
27191             tb.add(
27192                 '-', {
27193                     id:editorcore.frameId +'-forecolor',
27194                     cls:'x-btn-icon x-edit-forecolor',
27195                     clickEvent:'mousedown',
27196                     tooltip: this.buttonTips['forecolor'] || undefined,
27197                     tabIndex:-1,
27198                     menu : new Roo.menu.ColorMenu({
27199                         allowReselect: true,
27200                         focus: Roo.emptyFn,
27201                         value:'000000',
27202                         plain:true,
27203                         selectHandler: function(cp, color){
27204                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27205                             editor.deferFocus();
27206                         },
27207                         scope: editorcore,
27208                         clickEvent:'mousedown'
27209                     })
27210                 }, {
27211                     id:editorcore.frameId +'backcolor',
27212                     cls:'x-btn-icon x-edit-backcolor',
27213                     clickEvent:'mousedown',
27214                     tooltip: this.buttonTips['backcolor'] || undefined,
27215                     tabIndex:-1,
27216                     menu : new Roo.menu.ColorMenu({
27217                         focus: Roo.emptyFn,
27218                         value:'FFFFFF',
27219                         plain:true,
27220                         allowReselect: true,
27221                         selectHandler: function(cp, color){
27222                             if(Roo.isGecko){
27223                                 editorcore.execCmd('useCSS', false);
27224                                 editorcore.execCmd('hilitecolor', color);
27225                                 editorcore.execCmd('useCSS', true);
27226                                 editor.deferFocus();
27227                             }else{
27228                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27229                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27230                                 editor.deferFocus();
27231                             }
27232                         },
27233                         scope:editorcore,
27234                         clickEvent:'mousedown'
27235                     })
27236                 }
27237             );
27238         };
27239         // now add all the items...
27240         
27241
27242         if(!this.disable.alignments){
27243             tb.add(
27244                 '-',
27245                 btn('justifyleft'),
27246                 btn('justifycenter'),
27247                 btn('justifyright')
27248             );
27249         };
27250
27251         //if(!Roo.isSafari){
27252             if(!this.disable.links){
27253                 tb.add(
27254                     '-',
27255                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27256                 );
27257             };
27258
27259             if(!this.disable.lists){
27260                 tb.add(
27261                     '-',
27262                     btn('insertorderedlist'),
27263                     btn('insertunorderedlist')
27264                 );
27265             }
27266             if(!this.disable.sourceEdit){
27267                 tb.add(
27268                     '-',
27269                     btn('sourceedit', true, function(btn){
27270                         this.toggleSourceEdit(btn.pressed);
27271                     })
27272                 );
27273             }
27274         //}
27275         
27276         var smenu = { };
27277         // special menu.. - needs to be tidied up..
27278         if (!this.disable.special) {
27279             smenu = {
27280                 text: "&#169;",
27281                 cls: 'x-edit-none',
27282                 
27283                 menu : {
27284                     items : []
27285                 }
27286             };
27287             for (var i =0; i < this.specialChars.length; i++) {
27288                 smenu.menu.items.push({
27289                     
27290                     html: this.specialChars[i],
27291                     handler: function(a,b) {
27292                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27293                         //editor.insertAtCursor(a.html);
27294                         
27295                     },
27296                     tabIndex:-1
27297                 });
27298             }
27299             
27300             
27301             tb.add(smenu);
27302             
27303             
27304         }
27305         
27306         var cmenu = { };
27307         if (!this.disable.cleanStyles) {
27308             cmenu = {
27309                 cls: 'x-btn-icon x-btn-clear',
27310                 
27311                 menu : {
27312                     items : []
27313                 }
27314             };
27315             for (var i =0; i < this.cleanStyles.length; i++) {
27316                 cmenu.menu.items.push({
27317                     actiontype : this.cleanStyles[i],
27318                     html: 'Remove ' + this.cleanStyles[i],
27319                     handler: function(a,b) {
27320 //                        Roo.log(a);
27321 //                        Roo.log(b);
27322                         var c = Roo.get(editorcore.doc.body);
27323                         c.select('[style]').each(function(s) {
27324                             s.dom.style.removeProperty(a.actiontype);
27325                         });
27326                         editorcore.syncValue();
27327                     },
27328                     tabIndex:-1
27329                 });
27330             }
27331              cmenu.menu.items.push({
27332                 actiontype : 'tablewidths',
27333                 html: 'Remove Table Widths',
27334                 handler: function(a,b) {
27335                     editorcore.cleanTableWidths();
27336                     editorcore.syncValue();
27337                 },
27338                 tabIndex:-1
27339             });
27340             cmenu.menu.items.push({
27341                 actiontype : 'word',
27342                 html: 'Remove MS Word Formating',
27343                 handler: function(a,b) {
27344                     editorcore.cleanWord();
27345                     editorcore.syncValue();
27346                 },
27347                 tabIndex:-1
27348             });
27349             
27350             cmenu.menu.items.push({
27351                 actiontype : 'all',
27352                 html: 'Remove All Styles',
27353                 handler: function(a,b) {
27354                     
27355                     var c = Roo.get(editorcore.doc.body);
27356                     c.select('[style]').each(function(s) {
27357                         s.dom.removeAttribute('style');
27358                     });
27359                     editorcore.syncValue();
27360                 },
27361                 tabIndex:-1
27362             });
27363              cmenu.menu.items.push({
27364                 actiontype : 'tidy',
27365                 html: 'Tidy HTML Source',
27366                 handler: function(a,b) {
27367                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27368                     editorcore.syncValue();
27369                 },
27370                 tabIndex:-1
27371             });
27372             
27373             
27374             tb.add(cmenu);
27375         }
27376          
27377         if (!this.disable.specialElements) {
27378             var semenu = {
27379                 text: "Other;",
27380                 cls: 'x-edit-none',
27381                 menu : {
27382                     items : []
27383                 }
27384             };
27385             for (var i =0; i < this.specialElements.length; i++) {
27386                 semenu.menu.items.push(
27387                     Roo.apply({ 
27388                         handler: function(a,b) {
27389                             editor.insertAtCursor(this.ihtml);
27390                         }
27391                     }, this.specialElements[i])
27392                 );
27393                     
27394             }
27395             
27396             tb.add(semenu);
27397             
27398             
27399         }
27400          
27401         
27402         if (this.btns) {
27403             for(var i =0; i< this.btns.length;i++) {
27404                 var b = Roo.factory(this.btns[i],Roo.form);
27405                 b.cls =  'x-edit-none';
27406                 
27407                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27408                     b.cls += ' x-init-enable';
27409                 }
27410                 
27411                 b.scope = editorcore;
27412                 tb.add(b);
27413             }
27414         
27415         }
27416         
27417         
27418         
27419         // disable everything...
27420         
27421         this.tb.items.each(function(item){
27422             
27423            if(
27424                 item.id != editorcore.frameId+ '-sourceedit' && 
27425                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27426             ){
27427                 
27428                 item.disable();
27429             }
27430         });
27431         this.rendered = true;
27432         
27433         // the all the btns;
27434         editor.on('editorevent', this.updateToolbar, this);
27435         // other toolbars need to implement this..
27436         //editor.on('editmodechange', this.updateToolbar, this);
27437     },
27438     
27439     
27440     relayBtnCmd : function(btn) {
27441         this.editorcore.relayCmd(btn.cmd);
27442     },
27443     // private used internally
27444     createLink : function(){
27445         Roo.log("create link?");
27446         var url = prompt(this.createLinkText, this.defaultLinkValue);
27447         if(url && url != 'http:/'+'/'){
27448             this.editorcore.relayCmd('createlink', url);
27449         }
27450     },
27451
27452     
27453     /**
27454      * Protected method that will not generally be called directly. It triggers
27455      * a toolbar update by reading the markup state of the current selection in the editor.
27456      */
27457     updateToolbar: function(){
27458
27459         if(!this.editorcore.activated){
27460             this.editor.onFirstFocus();
27461             return;
27462         }
27463
27464         var btns = this.tb.items.map, 
27465             doc = this.editorcore.doc,
27466             frameId = this.editorcore.frameId;
27467
27468         if(!this.disable.font && !Roo.isSafari){
27469             /*
27470             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27471             if(name != this.fontSelect.dom.value){
27472                 this.fontSelect.dom.value = name;
27473             }
27474             */
27475         }
27476         if(!this.disable.format){
27477             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27478             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27479             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27480         }
27481         if(!this.disable.alignments){
27482             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27483             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27484             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27485         }
27486         if(!Roo.isSafari && !this.disable.lists){
27487             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27488             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27489         }
27490         
27491         var ans = this.editorcore.getAllAncestors();
27492         if (this.formatCombo) {
27493             
27494             
27495             var store = this.formatCombo.store;
27496             this.formatCombo.setValue("");
27497             for (var i =0; i < ans.length;i++) {
27498                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27499                     // select it..
27500                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27501                     break;
27502                 }
27503             }
27504         }
27505         
27506         
27507         
27508         // hides menus... - so this cant be on a menu...
27509         Roo.menu.MenuMgr.hideAll();
27510
27511         //this.editorsyncValue();
27512     },
27513    
27514     
27515     createFontOptions : function(){
27516         var buf = [], fs = this.fontFamilies, ff, lc;
27517         
27518         
27519         
27520         for(var i = 0, len = fs.length; i< len; i++){
27521             ff = fs[i];
27522             lc = ff.toLowerCase();
27523             buf.push(
27524                 '<option value="',lc,'" style="font-family:',ff,';"',
27525                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27526                     ff,
27527                 '</option>'
27528             );
27529         }
27530         return buf.join('');
27531     },
27532     
27533     toggleSourceEdit : function(sourceEditMode){
27534         
27535         Roo.log("toolbar toogle");
27536         if(sourceEditMode === undefined){
27537             sourceEditMode = !this.sourceEditMode;
27538         }
27539         this.sourceEditMode = sourceEditMode === true;
27540         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27541         // just toggle the button?
27542         if(btn.pressed !== this.sourceEditMode){
27543             btn.toggle(this.sourceEditMode);
27544             return;
27545         }
27546         
27547         if(sourceEditMode){
27548             Roo.log("disabling buttons");
27549             this.tb.items.each(function(item){
27550                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27551                     item.disable();
27552                 }
27553             });
27554           
27555         }else{
27556             Roo.log("enabling buttons");
27557             if(this.editorcore.initialized){
27558                 this.tb.items.each(function(item){
27559                     item.enable();
27560                 });
27561             }
27562             
27563         }
27564         Roo.log("calling toggole on editor");
27565         // tell the editor that it's been pressed..
27566         this.editor.toggleSourceEdit(sourceEditMode);
27567        
27568     },
27569      /**
27570      * Object collection of toolbar tooltips for the buttons in the editor. The key
27571      * is the command id associated with that button and the value is a valid QuickTips object.
27572      * For example:
27573 <pre><code>
27574 {
27575     bold : {
27576         title: 'Bold (Ctrl+B)',
27577         text: 'Make the selected text bold.',
27578         cls: 'x-html-editor-tip'
27579     },
27580     italic : {
27581         title: 'Italic (Ctrl+I)',
27582         text: 'Make the selected text italic.',
27583         cls: 'x-html-editor-tip'
27584     },
27585     ...
27586 </code></pre>
27587     * @type Object
27588      */
27589     buttonTips : {
27590         bold : {
27591             title: 'Bold (Ctrl+B)',
27592             text: 'Make the selected text bold.',
27593             cls: 'x-html-editor-tip'
27594         },
27595         italic : {
27596             title: 'Italic (Ctrl+I)',
27597             text: 'Make the selected text italic.',
27598             cls: 'x-html-editor-tip'
27599         },
27600         underline : {
27601             title: 'Underline (Ctrl+U)',
27602             text: 'Underline the selected text.',
27603             cls: 'x-html-editor-tip'
27604         },
27605         increasefontsize : {
27606             title: 'Grow Text',
27607             text: 'Increase the font size.',
27608             cls: 'x-html-editor-tip'
27609         },
27610         decreasefontsize : {
27611             title: 'Shrink Text',
27612             text: 'Decrease the font size.',
27613             cls: 'x-html-editor-tip'
27614         },
27615         backcolor : {
27616             title: 'Text Highlight Color',
27617             text: 'Change the background color of the selected text.',
27618             cls: 'x-html-editor-tip'
27619         },
27620         forecolor : {
27621             title: 'Font Color',
27622             text: 'Change the color of the selected text.',
27623             cls: 'x-html-editor-tip'
27624         },
27625         justifyleft : {
27626             title: 'Align Text Left',
27627             text: 'Align text to the left.',
27628             cls: 'x-html-editor-tip'
27629         },
27630         justifycenter : {
27631             title: 'Center Text',
27632             text: 'Center text in the editor.',
27633             cls: 'x-html-editor-tip'
27634         },
27635         justifyright : {
27636             title: 'Align Text Right',
27637             text: 'Align text to the right.',
27638             cls: 'x-html-editor-tip'
27639         },
27640         insertunorderedlist : {
27641             title: 'Bullet List',
27642             text: 'Start a bulleted list.',
27643             cls: 'x-html-editor-tip'
27644         },
27645         insertorderedlist : {
27646             title: 'Numbered List',
27647             text: 'Start a numbered list.',
27648             cls: 'x-html-editor-tip'
27649         },
27650         createlink : {
27651             title: 'Hyperlink',
27652             text: 'Make the selected text a hyperlink.',
27653             cls: 'x-html-editor-tip'
27654         },
27655         sourceedit : {
27656             title: 'Source Edit',
27657             text: 'Switch to source editing mode.',
27658             cls: 'x-html-editor-tip'
27659         }
27660     },
27661     // private
27662     onDestroy : function(){
27663         if(this.rendered){
27664             
27665             this.tb.items.each(function(item){
27666                 if(item.menu){
27667                     item.menu.removeAll();
27668                     if(item.menu.el){
27669                         item.menu.el.destroy();
27670                     }
27671                 }
27672                 item.destroy();
27673             });
27674              
27675         }
27676     },
27677     onFirstFocus: function() {
27678         this.tb.items.each(function(item){
27679            item.enable();
27680         });
27681     }
27682 });
27683
27684
27685
27686
27687 // <script type="text/javascript">
27688 /*
27689  * Based on
27690  * Ext JS Library 1.1.1
27691  * Copyright(c) 2006-2007, Ext JS, LLC.
27692  *  
27693  
27694  */
27695
27696  
27697 /**
27698  * @class Roo.form.HtmlEditor.ToolbarContext
27699  * Context Toolbar
27700  * 
27701  * Usage:
27702  *
27703  new Roo.form.HtmlEditor({
27704     ....
27705     toolbars : [
27706         { xtype: 'ToolbarStandard', styles : {} }
27707         { xtype: 'ToolbarContext', disable : {} }
27708     ]
27709 })
27710
27711      
27712  * 
27713  * @config : {Object} disable List of elements to disable.. (not done yet.)
27714  * @config : {Object} styles  Map of styles available.
27715  * 
27716  */
27717
27718 Roo.form.HtmlEditor.ToolbarContext = function(config)
27719 {
27720     
27721     Roo.apply(this, config);
27722     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27723     // dont call parent... till later.
27724     this.styles = this.styles || {};
27725 }
27726
27727  
27728
27729 Roo.form.HtmlEditor.ToolbarContext.types = {
27730     'IMG' : {
27731         width : {
27732             title: "Width",
27733             width: 40
27734         },
27735         height:  {
27736             title: "Height",
27737             width: 40
27738         },
27739         align: {
27740             title: "Align",
27741             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27742             width : 80
27743             
27744         },
27745         border: {
27746             title: "Border",
27747             width: 40
27748         },
27749         alt: {
27750             title: "Alt",
27751             width: 120
27752         },
27753         src : {
27754             title: "Src",
27755             width: 220
27756         }
27757         
27758     },
27759     'A' : {
27760         name : {
27761             title: "Name",
27762             width: 50
27763         },
27764         target:  {
27765             title: "Target",
27766             width: 120
27767         },
27768         href:  {
27769             title: "Href",
27770             width: 220
27771         } // border?
27772         
27773     },
27774     'TABLE' : {
27775         rows : {
27776             title: "Rows",
27777             width: 20
27778         },
27779         cols : {
27780             title: "Cols",
27781             width: 20
27782         },
27783         width : {
27784             title: "Width",
27785             width: 40
27786         },
27787         height : {
27788             title: "Height",
27789             width: 40
27790         },
27791         border : {
27792             title: "Border",
27793             width: 20
27794         }
27795     },
27796     'TD' : {
27797         width : {
27798             title: "Width",
27799             width: 40
27800         },
27801         height : {
27802             title: "Height",
27803             width: 40
27804         },   
27805         align: {
27806             title: "Align",
27807             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27808             width: 80
27809         },
27810         valign: {
27811             title: "Valign",
27812             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27813             width: 80
27814         },
27815         colspan: {
27816             title: "Colspan",
27817             width: 20
27818             
27819         },
27820          'font-family'  : {
27821             title : "Font",
27822             style : 'fontFamily',
27823             displayField: 'display',
27824             optname : 'font-family',
27825             width: 140
27826         }
27827     },
27828     'INPUT' : {
27829         name : {
27830             title: "name",
27831             width: 120
27832         },
27833         value : {
27834             title: "Value",
27835             width: 120
27836         },
27837         width : {
27838             title: "Width",
27839             width: 40
27840         }
27841     },
27842     'LABEL' : {
27843         'for' : {
27844             title: "For",
27845             width: 120
27846         }
27847     },
27848     'TEXTAREA' : {
27849           name : {
27850             title: "name",
27851             width: 120
27852         },
27853         rows : {
27854             title: "Rows",
27855             width: 20
27856         },
27857         cols : {
27858             title: "Cols",
27859             width: 20
27860         }
27861     },
27862     'SELECT' : {
27863         name : {
27864             title: "name",
27865             width: 120
27866         },
27867         selectoptions : {
27868             title: "Options",
27869             width: 200
27870         }
27871     },
27872     
27873     // should we really allow this??
27874     // should this just be 
27875     'BODY' : {
27876         title : {
27877             title: "Title",
27878             width: 200,
27879             disabled : true
27880         }
27881     },
27882     'SPAN' : {
27883         'font-family'  : {
27884             title : "Font",
27885             style : 'fontFamily',
27886             displayField: 'display',
27887             optname : 'font-family',
27888             width: 140
27889         }
27890     },
27891     'DIV' : {
27892         'font-family'  : {
27893             title : "Font",
27894             style : 'fontFamily',
27895             displayField: 'display',
27896             optname : 'font-family',
27897             width: 140
27898         }
27899     },
27900      'P' : {
27901         'font-family'  : {
27902             title : "Font",
27903             style : 'fontFamily',
27904             displayField: 'display',
27905             optname : 'font-family',
27906             width: 140
27907         }
27908     },
27909     
27910     '*' : {
27911         // empty..
27912     }
27913
27914 };
27915
27916 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27917 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27918
27919 Roo.form.HtmlEditor.ToolbarContext.options = {
27920         'font-family'  : [ 
27921                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27922                 [ 'Courier New', 'Courier New'],
27923                 [ 'Tahoma', 'Tahoma'],
27924                 [ 'Times New Roman,serif', 'Times'],
27925                 [ 'Verdana','Verdana' ]
27926         ]
27927 };
27928
27929 // fixme - these need to be configurable..
27930  
27931
27932 Roo.form.HtmlEditor.ToolbarContext.types
27933
27934
27935 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27936     
27937     tb: false,
27938     
27939     rendered: false,
27940     
27941     editor : false,
27942     editorcore : false,
27943     /**
27944      * @cfg {Object} disable  List of toolbar elements to disable
27945          
27946      */
27947     disable : false,
27948     /**
27949      * @cfg {Object} styles List of styles 
27950      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27951      *
27952      * These must be defined in the page, so they get rendered correctly..
27953      * .headline { }
27954      * TD.underline { }
27955      * 
27956      */
27957     styles : false,
27958     
27959     options: false,
27960     
27961     toolbars : false,
27962     
27963     init : function(editor)
27964     {
27965         this.editor = editor;
27966         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27967         var editorcore = this.editorcore;
27968         
27969         var fid = editorcore.frameId;
27970         var etb = this;
27971         function btn(id, toggle, handler){
27972             var xid = fid + '-'+ id ;
27973             return {
27974                 id : xid,
27975                 cmd : id,
27976                 cls : 'x-btn-icon x-edit-'+id,
27977                 enableToggle:toggle !== false,
27978                 scope: editorcore, // was editor...
27979                 handler:handler||editorcore.relayBtnCmd,
27980                 clickEvent:'mousedown',
27981                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27982                 tabIndex:-1
27983             };
27984         }
27985         // create a new element.
27986         var wdiv = editor.wrap.createChild({
27987                 tag: 'div'
27988             }, editor.wrap.dom.firstChild.nextSibling, true);
27989         
27990         // can we do this more than once??
27991         
27992          // stop form submits
27993       
27994  
27995         // disable everything...
27996         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27997         this.toolbars = {};
27998            
27999         for (var i in  ty) {
28000           
28001             this.toolbars[i] = this.buildToolbar(ty[i],i);
28002         }
28003         this.tb = this.toolbars.BODY;
28004         this.tb.el.show();
28005         this.buildFooter();
28006         this.footer.show();
28007         editor.on('hide', function( ) { this.footer.hide() }, this);
28008         editor.on('show', function( ) { this.footer.show() }, this);
28009         
28010          
28011         this.rendered = true;
28012         
28013         // the all the btns;
28014         editor.on('editorevent', this.updateToolbar, this);
28015         // other toolbars need to implement this..
28016         //editor.on('editmodechange', this.updateToolbar, this);
28017     },
28018     
28019     
28020     
28021     /**
28022      * Protected method that will not generally be called directly. It triggers
28023      * a toolbar update by reading the markup state of the current selection in the editor.
28024      *
28025      * Note you can force an update by calling on('editorevent', scope, false)
28026      */
28027     updateToolbar: function(editor,ev,sel){
28028
28029         //Roo.log(ev);
28030         // capture mouse up - this is handy for selecting images..
28031         // perhaps should go somewhere else...
28032         if(!this.editorcore.activated){
28033              this.editor.onFirstFocus();
28034             return;
28035         }
28036         
28037         
28038         
28039         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28040         // selectNode - might want to handle IE?
28041         if (ev &&
28042             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28043             ev.target && ev.target.tagName == 'IMG') {
28044             // they have click on an image...
28045             // let's see if we can change the selection...
28046             sel = ev.target;
28047          
28048               var nodeRange = sel.ownerDocument.createRange();
28049             try {
28050                 nodeRange.selectNode(sel);
28051             } catch (e) {
28052                 nodeRange.selectNodeContents(sel);
28053             }
28054             //nodeRange.collapse(true);
28055             var s = this.editorcore.win.getSelection();
28056             s.removeAllRanges();
28057             s.addRange(nodeRange);
28058         }  
28059         
28060       
28061         var updateFooter = sel ? false : true;
28062         
28063         
28064         var ans = this.editorcore.getAllAncestors();
28065         
28066         // pick
28067         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28068         
28069         if (!sel) { 
28070             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28071             sel = sel ? sel : this.editorcore.doc.body;
28072             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28073             
28074         }
28075         // pick a menu that exists..
28076         var tn = sel.tagName.toUpperCase();
28077         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28078         
28079         tn = sel.tagName.toUpperCase();
28080         
28081         var lastSel = this.tb.selectedNode
28082         
28083         this.tb.selectedNode = sel;
28084         
28085         // if current menu does not match..
28086         
28087         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28088                 
28089             this.tb.el.hide();
28090             ///console.log("show: " + tn);
28091             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28092             this.tb.el.show();
28093             // update name
28094             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28095             
28096             
28097             // update attributes
28098             if (this.tb.fields) {
28099                 this.tb.fields.each(function(e) {
28100                     if (e.stylename) {
28101                         e.setValue(sel.style[e.stylename]);
28102                         return;
28103                     } 
28104                    e.setValue(sel.getAttribute(e.attrname));
28105                 });
28106             }
28107             
28108             var hasStyles = false;
28109             for(var i in this.styles) {
28110                 hasStyles = true;
28111                 break;
28112             }
28113             
28114             // update styles
28115             if (hasStyles) { 
28116                 var st = this.tb.fields.item(0);
28117                 
28118                 st.store.removeAll();
28119                
28120                 
28121                 var cn = sel.className.split(/\s+/);
28122                 
28123                 var avs = [];
28124                 if (this.styles['*']) {
28125                     
28126                     Roo.each(this.styles['*'], function(v) {
28127                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28128                     });
28129                 }
28130                 if (this.styles[tn]) { 
28131                     Roo.each(this.styles[tn], function(v) {
28132                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28133                     });
28134                 }
28135                 
28136                 st.store.loadData(avs);
28137                 st.collapse();
28138                 st.setValue(cn);
28139             }
28140             // flag our selected Node.
28141             this.tb.selectedNode = sel;
28142            
28143            
28144             Roo.menu.MenuMgr.hideAll();
28145
28146         }
28147         
28148         if (!updateFooter) {
28149             //this.footDisp.dom.innerHTML = ''; 
28150             return;
28151         }
28152         // update the footer
28153         //
28154         var html = '';
28155         
28156         this.footerEls = ans.reverse();
28157         Roo.each(this.footerEls, function(a,i) {
28158             if (!a) { return; }
28159             html += html.length ? ' &gt; '  :  '';
28160             
28161             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28162             
28163         });
28164        
28165         // 
28166         var sz = this.footDisp.up('td').getSize();
28167         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28168         this.footDisp.dom.style.marginLeft = '5px';
28169         
28170         this.footDisp.dom.style.overflow = 'hidden';
28171         
28172         this.footDisp.dom.innerHTML = html;
28173             
28174         //this.editorsyncValue();
28175     },
28176      
28177     
28178    
28179        
28180     // private
28181     onDestroy : function(){
28182         if(this.rendered){
28183             
28184             this.tb.items.each(function(item){
28185                 if(item.menu){
28186                     item.menu.removeAll();
28187                     if(item.menu.el){
28188                         item.menu.el.destroy();
28189                     }
28190                 }
28191                 item.destroy();
28192             });
28193              
28194         }
28195     },
28196     onFirstFocus: function() {
28197         // need to do this for all the toolbars..
28198         this.tb.items.each(function(item){
28199            item.enable();
28200         });
28201     },
28202     buildToolbar: function(tlist, nm)
28203     {
28204         var editor = this.editor;
28205         var editorcore = this.editorcore;
28206          // create a new element.
28207         var wdiv = editor.wrap.createChild({
28208                 tag: 'div'
28209             }, editor.wrap.dom.firstChild.nextSibling, true);
28210         
28211        
28212         var tb = new Roo.Toolbar(wdiv);
28213         // add the name..
28214         
28215         tb.add(nm+ ":&nbsp;");
28216         
28217         var styles = [];
28218         for(var i in this.styles) {
28219             styles.push(i);
28220         }
28221         
28222         // styles...
28223         if (styles && styles.length) {
28224             
28225             // this needs a multi-select checkbox...
28226             tb.addField( new Roo.form.ComboBox({
28227                 store: new Roo.data.SimpleStore({
28228                     id : 'val',
28229                     fields: ['val', 'selected'],
28230                     data : [] 
28231                 }),
28232                 name : '-roo-edit-className',
28233                 attrname : 'className',
28234                 displayField: 'val',
28235                 typeAhead: false,
28236                 mode: 'local',
28237                 editable : false,
28238                 triggerAction: 'all',
28239                 emptyText:'Select Style',
28240                 selectOnFocus:true,
28241                 width: 130,
28242                 listeners : {
28243                     'select': function(c, r, i) {
28244                         // initial support only for on class per el..
28245                         tb.selectedNode.className =  r ? r.get('val') : '';
28246                         editorcore.syncValue();
28247                     }
28248                 }
28249     
28250             }));
28251         }
28252         
28253         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28254         var tbops = tbc.options;
28255         
28256         for (var i in tlist) {
28257             
28258             var item = tlist[i];
28259             tb.add(item.title + ":&nbsp;");
28260             
28261             
28262             //optname == used so you can configure the options available..
28263             var opts = item.opts ? item.opts : false;
28264             if (item.optname) {
28265                 opts = tbops[item.optname];
28266            
28267             }
28268             
28269             if (opts) {
28270                 // opts == pulldown..
28271                 tb.addField( new Roo.form.ComboBox({
28272                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28273                         id : 'val',
28274                         fields: ['val', 'display'],
28275                         data : opts  
28276                     }),
28277                     name : '-roo-edit-' + i,
28278                     attrname : i,
28279                     stylename : item.style ? item.style : false,
28280                     displayField: item.displayField ? item.displayField : 'val',
28281                     valueField :  'val',
28282                     typeAhead: false,
28283                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28284                     editable : false,
28285                     triggerAction: 'all',
28286                     emptyText:'Select',
28287                     selectOnFocus:true,
28288                     width: item.width ? item.width  : 130,
28289                     listeners : {
28290                         'select': function(c, r, i) {
28291                             if (c.stylename) {
28292                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28293                                 return;
28294                             }
28295                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28296                         }
28297                     }
28298
28299                 }));
28300                 continue;
28301                     
28302                  
28303                 
28304                 tb.addField( new Roo.form.TextField({
28305                     name: i,
28306                     width: 100,
28307                     //allowBlank:false,
28308                     value: ''
28309                 }));
28310                 continue;
28311             }
28312             tb.addField( new Roo.form.TextField({
28313                 name: '-roo-edit-' + i,
28314                 attrname : i,
28315                 
28316                 width: item.width,
28317                 //allowBlank:true,
28318                 value: '',
28319                 listeners: {
28320                     'change' : function(f, nv, ov) {
28321                         tb.selectedNode.setAttribute(f.attrname, nv);
28322                     }
28323                 }
28324             }));
28325              
28326         }
28327         
28328         var _this = this;
28329         
28330         if(nm == 'BODY'){
28331             tb.addSeparator();
28332         
28333             tb.addButton( {
28334                 text: 'Stylesheets',
28335
28336                 listeners : {
28337                     click : function ()
28338                     {
28339                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28340                     }
28341                 }
28342             });
28343         }
28344         
28345         tb.addFill();
28346         tb.addButton( {
28347             text: 'Remove Tag',
28348     
28349             listeners : {
28350                 click : function ()
28351                 {
28352                     // remove
28353                     // undo does not work.
28354                      
28355                     var sn = tb.selectedNode;
28356                     
28357                     var pn = sn.parentNode;
28358                     
28359                     var stn =  sn.childNodes[0];
28360                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28361                     while (sn.childNodes.length) {
28362                         var node = sn.childNodes[0];
28363                         sn.removeChild(node);
28364                         //Roo.log(node);
28365                         pn.insertBefore(node, sn);
28366                         
28367                     }
28368                     pn.removeChild(sn);
28369                     var range = editorcore.createRange();
28370         
28371                     range.setStart(stn,0);
28372                     range.setEnd(en,0); //????
28373                     //range.selectNode(sel);
28374                     
28375                     
28376                     var selection = editorcore.getSelection();
28377                     selection.removeAllRanges();
28378                     selection.addRange(range);
28379                     
28380                     
28381                     
28382                     //_this.updateToolbar(null, null, pn);
28383                     _this.updateToolbar(null, null, null);
28384                     _this.footDisp.dom.innerHTML = ''; 
28385                 }
28386             }
28387             
28388                     
28389                 
28390             
28391         });
28392         
28393         
28394         tb.el.on('click', function(e){
28395             e.preventDefault(); // what does this do?
28396         });
28397         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28398         tb.el.hide();
28399         tb.name = nm;
28400         // dont need to disable them... as they will get hidden
28401         return tb;
28402          
28403         
28404     },
28405     buildFooter : function()
28406     {
28407         
28408         var fel = this.editor.wrap.createChild();
28409         this.footer = new Roo.Toolbar(fel);
28410         // toolbar has scrolly on left / right?
28411         var footDisp= new Roo.Toolbar.Fill();
28412         var _t = this;
28413         this.footer.add(
28414             {
28415                 text : '&lt;',
28416                 xtype: 'Button',
28417                 handler : function() {
28418                     _t.footDisp.scrollTo('left',0,true)
28419                 }
28420             }
28421         );
28422         this.footer.add( footDisp );
28423         this.footer.add( 
28424             {
28425                 text : '&gt;',
28426                 xtype: 'Button',
28427                 handler : function() {
28428                     // no animation..
28429                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28430                 }
28431             }
28432         );
28433         var fel = Roo.get(footDisp.el);
28434         fel.addClass('x-editor-context');
28435         this.footDispWrap = fel; 
28436         this.footDispWrap.overflow  = 'hidden';
28437         
28438         this.footDisp = fel.createChild();
28439         this.footDispWrap.on('click', this.onContextClick, this)
28440         
28441         
28442     },
28443     onContextClick : function (ev,dom)
28444     {
28445         ev.preventDefault();
28446         var  cn = dom.className;
28447         //Roo.log(cn);
28448         if (!cn.match(/x-ed-loc-/)) {
28449             return;
28450         }
28451         var n = cn.split('-').pop();
28452         var ans = this.footerEls;
28453         var sel = ans[n];
28454         
28455          // pick
28456         var range = this.editorcore.createRange();
28457         
28458         range.selectNodeContents(sel);
28459         //range.selectNode(sel);
28460         
28461         
28462         var selection = this.editorcore.getSelection();
28463         selection.removeAllRanges();
28464         selection.addRange(range);
28465         
28466         
28467         
28468         this.updateToolbar(null, null, sel);
28469         
28470         
28471     }
28472     
28473     
28474     
28475     
28476     
28477 });
28478
28479
28480
28481
28482
28483 /*
28484  * Based on:
28485  * Ext JS Library 1.1.1
28486  * Copyright(c) 2006-2007, Ext JS, LLC.
28487  *
28488  * Originally Released Under LGPL - original licence link has changed is not relivant.
28489  *
28490  * Fork - LGPL
28491  * <script type="text/javascript">
28492  */
28493  
28494 /**
28495  * @class Roo.form.BasicForm
28496  * @extends Roo.util.Observable
28497  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28498  * @constructor
28499  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28500  * @param {Object} config Configuration options
28501  */
28502 Roo.form.BasicForm = function(el, config){
28503     this.allItems = [];
28504     this.childForms = [];
28505     Roo.apply(this, config);
28506     /*
28507      * The Roo.form.Field items in this form.
28508      * @type MixedCollection
28509      */
28510      
28511      
28512     this.items = new Roo.util.MixedCollection(false, function(o){
28513         return o.id || (o.id = Roo.id());
28514     });
28515     this.addEvents({
28516         /**
28517          * @event beforeaction
28518          * Fires before any action is performed. Return false to cancel the action.
28519          * @param {Form} this
28520          * @param {Action} action The action to be performed
28521          */
28522         beforeaction: true,
28523         /**
28524          * @event actionfailed
28525          * Fires when an action fails.
28526          * @param {Form} this
28527          * @param {Action} action The action that failed
28528          */
28529         actionfailed : true,
28530         /**
28531          * @event actioncomplete
28532          * Fires when an action is completed.
28533          * @param {Form} this
28534          * @param {Action} action The action that completed
28535          */
28536         actioncomplete : true
28537     });
28538     if(el){
28539         this.initEl(el);
28540     }
28541     Roo.form.BasicForm.superclass.constructor.call(this);
28542 };
28543
28544 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28545     /**
28546      * @cfg {String} method
28547      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28548      */
28549     /**
28550      * @cfg {DataReader} reader
28551      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28552      * This is optional as there is built-in support for processing JSON.
28553      */
28554     /**
28555      * @cfg {DataReader} errorReader
28556      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28557      * This is completely optional as there is built-in support for processing JSON.
28558      */
28559     /**
28560      * @cfg {String} url
28561      * The URL to use for form actions if one isn't supplied in the action options.
28562      */
28563     /**
28564      * @cfg {Boolean} fileUpload
28565      * Set to true if this form is a file upload.
28566      */
28567      
28568     /**
28569      * @cfg {Object} baseParams
28570      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28571      */
28572      /**
28573      
28574     /**
28575      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28576      */
28577     timeout: 30,
28578
28579     // private
28580     activeAction : null,
28581
28582     /**
28583      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28584      * or setValues() data instead of when the form was first created.
28585      */
28586     trackResetOnLoad : false,
28587     
28588     
28589     /**
28590      * childForms - used for multi-tab forms
28591      * @type {Array}
28592      */
28593     childForms : false,
28594     
28595     /**
28596      * allItems - full list of fields.
28597      * @type {Array}
28598      */
28599     allItems : false,
28600     
28601     /**
28602      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28603      * element by passing it or its id or mask the form itself by passing in true.
28604      * @type Mixed
28605      */
28606     waitMsgTarget : false,
28607
28608     // private
28609     initEl : function(el){
28610         this.el = Roo.get(el);
28611         this.id = this.el.id || Roo.id();
28612         this.el.on('submit', this.onSubmit, this);
28613         this.el.addClass('x-form');
28614     },
28615
28616     // private
28617     onSubmit : function(e){
28618         e.stopEvent();
28619     },
28620
28621     /**
28622      * Returns true if client-side validation on the form is successful.
28623      * @return Boolean
28624      */
28625     isValid : function(){
28626         var valid = true;
28627         this.items.each(function(f){
28628            if(!f.validate()){
28629                valid = false;
28630            }
28631         });
28632         return valid;
28633     },
28634
28635     /**
28636      * Returns true if any fields in this form have changed since their original load.
28637      * @return Boolean
28638      */
28639     isDirty : function(){
28640         var dirty = false;
28641         this.items.each(function(f){
28642            if(f.isDirty()){
28643                dirty = true;
28644                return false;
28645            }
28646         });
28647         return dirty;
28648     },
28649
28650     /**
28651      * Performs a predefined action (submit or load) or custom actions you define on this form.
28652      * @param {String} actionName The name of the action type
28653      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28654      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28655      * accept other config options):
28656      * <pre>
28657 Property          Type             Description
28658 ----------------  ---------------  ----------------------------------------------------------------------------------
28659 url               String           The url for the action (defaults to the form's url)
28660 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28661 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28662 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28663                                    validate the form on the client (defaults to false)
28664      * </pre>
28665      * @return {BasicForm} this
28666      */
28667     doAction : function(action, options){
28668         if(typeof action == 'string'){
28669             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28670         }
28671         if(this.fireEvent('beforeaction', this, action) !== false){
28672             this.beforeAction(action);
28673             action.run.defer(100, action);
28674         }
28675         return this;
28676     },
28677
28678     /**
28679      * Shortcut to do a submit action.
28680      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28681      * @return {BasicForm} this
28682      */
28683     submit : function(options){
28684         this.doAction('submit', options);
28685         return this;
28686     },
28687
28688     /**
28689      * Shortcut to do a load action.
28690      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28691      * @return {BasicForm} this
28692      */
28693     load : function(options){
28694         this.doAction('load', options);
28695         return this;
28696     },
28697
28698     /**
28699      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28700      * @param {Record} record The record to edit
28701      * @return {BasicForm} this
28702      */
28703     updateRecord : function(record){
28704         record.beginEdit();
28705         var fs = record.fields;
28706         fs.each(function(f){
28707             var field = this.findField(f.name);
28708             if(field){
28709                 record.set(f.name, field.getValue());
28710             }
28711         }, this);
28712         record.endEdit();
28713         return this;
28714     },
28715
28716     /**
28717      * Loads an Roo.data.Record into this form.
28718      * @param {Record} record The record to load
28719      * @return {BasicForm} this
28720      */
28721     loadRecord : function(record){
28722         this.setValues(record.data);
28723         return this;
28724     },
28725
28726     // private
28727     beforeAction : function(action){
28728         var o = action.options;
28729         
28730        
28731         if(this.waitMsgTarget === true){
28732             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28733         }else if(this.waitMsgTarget){
28734             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28735             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28736         }else {
28737             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28738         }
28739          
28740     },
28741
28742     // private
28743     afterAction : function(action, success){
28744         this.activeAction = null;
28745         var o = action.options;
28746         
28747         if(this.waitMsgTarget === true){
28748             this.el.unmask();
28749         }else if(this.waitMsgTarget){
28750             this.waitMsgTarget.unmask();
28751         }else{
28752             Roo.MessageBox.updateProgress(1);
28753             Roo.MessageBox.hide();
28754         }
28755          
28756         if(success){
28757             if(o.reset){
28758                 this.reset();
28759             }
28760             Roo.callback(o.success, o.scope, [this, action]);
28761             this.fireEvent('actioncomplete', this, action);
28762             
28763         }else{
28764             
28765             // failure condition..
28766             // we have a scenario where updates need confirming.
28767             // eg. if a locking scenario exists..
28768             // we look for { errors : { needs_confirm : true }} in the response.
28769             if (
28770                 (typeof(action.result) != 'undefined')  &&
28771                 (typeof(action.result.errors) != 'undefined')  &&
28772                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28773            ){
28774                 var _t = this;
28775                 Roo.MessageBox.confirm(
28776                     "Change requires confirmation",
28777                     action.result.errorMsg,
28778                     function(r) {
28779                         if (r != 'yes') {
28780                             return;
28781                         }
28782                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28783                     }
28784                     
28785                 );
28786                 
28787                 
28788                 
28789                 return;
28790             }
28791             
28792             Roo.callback(o.failure, o.scope, [this, action]);
28793             // show an error message if no failed handler is set..
28794             if (!this.hasListener('actionfailed')) {
28795                 Roo.MessageBox.alert("Error",
28796                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28797                         action.result.errorMsg :
28798                         "Saving Failed, please check your entries or try again"
28799                 );
28800             }
28801             
28802             this.fireEvent('actionfailed', this, action);
28803         }
28804         
28805     },
28806
28807     /**
28808      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28809      * @param {String} id The value to search for
28810      * @return Field
28811      */
28812     findField : function(id){
28813         var field = this.items.get(id);
28814         if(!field){
28815             this.items.each(function(f){
28816                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28817                     field = f;
28818                     return false;
28819                 }
28820             });
28821         }
28822         return field || null;
28823     },
28824
28825     /**
28826      * Add a secondary form to this one, 
28827      * Used to provide tabbed forms. One form is primary, with hidden values 
28828      * which mirror the elements from the other forms.
28829      * 
28830      * @param {Roo.form.Form} form to add.
28831      * 
28832      */
28833     addForm : function(form)
28834     {
28835        
28836         if (this.childForms.indexOf(form) > -1) {
28837             // already added..
28838             return;
28839         }
28840         this.childForms.push(form);
28841         var n = '';
28842         Roo.each(form.allItems, function (fe) {
28843             
28844             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28845             if (this.findField(n)) { // already added..
28846                 return;
28847             }
28848             var add = new Roo.form.Hidden({
28849                 name : n
28850             });
28851             add.render(this.el);
28852             
28853             this.add( add );
28854         }, this);
28855         
28856     },
28857     /**
28858      * Mark fields in this form invalid in bulk.
28859      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28860      * @return {BasicForm} this
28861      */
28862     markInvalid : function(errors){
28863         if(errors instanceof Array){
28864             for(var i = 0, len = errors.length; i < len; i++){
28865                 var fieldError = errors[i];
28866                 var f = this.findField(fieldError.id);
28867                 if(f){
28868                     f.markInvalid(fieldError.msg);
28869                 }
28870             }
28871         }else{
28872             var field, id;
28873             for(id in errors){
28874                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28875                     field.markInvalid(errors[id]);
28876                 }
28877             }
28878         }
28879         Roo.each(this.childForms || [], function (f) {
28880             f.markInvalid(errors);
28881         });
28882         
28883         return this;
28884     },
28885
28886     /**
28887      * Set values for fields in this form in bulk.
28888      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28889      * @return {BasicForm} this
28890      */
28891     setValues : function(values){
28892         if(values instanceof Array){ // array of objects
28893             for(var i = 0, len = values.length; i < len; i++){
28894                 var v = values[i];
28895                 var f = this.findField(v.id);
28896                 if(f){
28897                     f.setValue(v.value);
28898                     if(this.trackResetOnLoad){
28899                         f.originalValue = f.getValue();
28900                     }
28901                 }
28902             }
28903         }else{ // object hash
28904             var field, id;
28905             for(id in values){
28906                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28907                     
28908                     if (field.setFromData && 
28909                         field.valueField && 
28910                         field.displayField &&
28911                         // combos' with local stores can 
28912                         // be queried via setValue()
28913                         // to set their value..
28914                         (field.store && !field.store.isLocal)
28915                         ) {
28916                         // it's a combo
28917                         var sd = { };
28918                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28919                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28920                         field.setFromData(sd);
28921                         
28922                     } else {
28923                         field.setValue(values[id]);
28924                     }
28925                     
28926                     
28927                     if(this.trackResetOnLoad){
28928                         field.originalValue = field.getValue();
28929                     }
28930                 }
28931             }
28932         }
28933          
28934         Roo.each(this.childForms || [], function (f) {
28935             f.setValues(values);
28936         });
28937                 
28938         return this;
28939     },
28940
28941     /**
28942      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28943      * they are returned as an array.
28944      * @param {Boolean} asString
28945      * @return {Object}
28946      */
28947     getValues : function(asString){
28948         if (this.childForms) {
28949             // copy values from the child forms
28950             Roo.each(this.childForms, function (f) {
28951                 this.setValues(f.getValues());
28952             }, this);
28953         }
28954         
28955         
28956         
28957         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28958         if(asString === true){
28959             return fs;
28960         }
28961         return Roo.urlDecode(fs);
28962     },
28963     
28964     /**
28965      * Returns the fields in this form as an object with key/value pairs. 
28966      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28967      * @return {Object}
28968      */
28969     getFieldValues : function(with_hidden)
28970     {
28971         if (this.childForms) {
28972             // copy values from the child forms
28973             // should this call getFieldValues - probably not as we do not currently copy
28974             // hidden fields when we generate..
28975             Roo.each(this.childForms, function (f) {
28976                 this.setValues(f.getValues());
28977             }, this);
28978         }
28979         
28980         var ret = {};
28981         this.items.each(function(f){
28982             if (!f.getName()) {
28983                 return;
28984             }
28985             var v = f.getValue();
28986             if (f.inputType =='radio') {
28987                 if (typeof(ret[f.getName()]) == 'undefined') {
28988                     ret[f.getName()] = ''; // empty..
28989                 }
28990                 
28991                 if (!f.el.dom.checked) {
28992                     return;
28993                     
28994                 }
28995                 v = f.el.dom.value;
28996                 
28997             }
28998             
28999             // not sure if this supported any more..
29000             if ((typeof(v) == 'object') && f.getRawValue) {
29001                 v = f.getRawValue() ; // dates..
29002             }
29003             // combo boxes where name != hiddenName...
29004             if (f.name != f.getName()) {
29005                 ret[f.name] = f.getRawValue();
29006             }
29007             ret[f.getName()] = v;
29008         });
29009         
29010         return ret;
29011     },
29012
29013     /**
29014      * Clears all invalid messages in this form.
29015      * @return {BasicForm} this
29016      */
29017     clearInvalid : function(){
29018         this.items.each(function(f){
29019            f.clearInvalid();
29020         });
29021         
29022         Roo.each(this.childForms || [], function (f) {
29023             f.clearInvalid();
29024         });
29025         
29026         
29027         return this;
29028     },
29029
29030     /**
29031      * Resets this form.
29032      * @return {BasicForm} this
29033      */
29034     reset : function(){
29035         this.items.each(function(f){
29036             f.reset();
29037         });
29038         
29039         Roo.each(this.childForms || [], function (f) {
29040             f.reset();
29041         });
29042        
29043         
29044         return this;
29045     },
29046
29047     /**
29048      * Add Roo.form components to this form.
29049      * @param {Field} field1
29050      * @param {Field} field2 (optional)
29051      * @param {Field} etc (optional)
29052      * @return {BasicForm} this
29053      */
29054     add : function(){
29055         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29056         return this;
29057     },
29058
29059
29060     /**
29061      * Removes a field from the items collection (does NOT remove its markup).
29062      * @param {Field} field
29063      * @return {BasicForm} this
29064      */
29065     remove : function(field){
29066         this.items.remove(field);
29067         return this;
29068     },
29069
29070     /**
29071      * Looks at the fields in this form, checks them for an id attribute,
29072      * and calls applyTo on the existing dom element with that id.
29073      * @return {BasicForm} this
29074      */
29075     render : function(){
29076         this.items.each(function(f){
29077             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29078                 f.applyTo(f.id);
29079             }
29080         });
29081         return this;
29082     },
29083
29084     /**
29085      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29086      * @param {Object} values
29087      * @return {BasicForm} this
29088      */
29089     applyToFields : function(o){
29090         this.items.each(function(f){
29091            Roo.apply(f, o);
29092         });
29093         return this;
29094     },
29095
29096     /**
29097      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29098      * @param {Object} values
29099      * @return {BasicForm} this
29100      */
29101     applyIfToFields : function(o){
29102         this.items.each(function(f){
29103            Roo.applyIf(f, o);
29104         });
29105         return this;
29106     }
29107 });
29108
29109 // back compat
29110 Roo.BasicForm = Roo.form.BasicForm;/*
29111  * Based on:
29112  * Ext JS Library 1.1.1
29113  * Copyright(c) 2006-2007, Ext JS, LLC.
29114  *
29115  * Originally Released Under LGPL - original licence link has changed is not relivant.
29116  *
29117  * Fork - LGPL
29118  * <script type="text/javascript">
29119  */
29120
29121 /**
29122  * @class Roo.form.Form
29123  * @extends Roo.form.BasicForm
29124  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29125  * @constructor
29126  * @param {Object} config Configuration options
29127  */
29128 Roo.form.Form = function(config){
29129     var xitems =  [];
29130     if (config.items) {
29131         xitems = config.items;
29132         delete config.items;
29133     }
29134    
29135     
29136     Roo.form.Form.superclass.constructor.call(this, null, config);
29137     this.url = this.url || this.action;
29138     if(!this.root){
29139         this.root = new Roo.form.Layout(Roo.applyIf({
29140             id: Roo.id()
29141         }, config));
29142     }
29143     this.active = this.root;
29144     /**
29145      * Array of all the buttons that have been added to this form via {@link addButton}
29146      * @type Array
29147      */
29148     this.buttons = [];
29149     this.allItems = [];
29150     this.addEvents({
29151         /**
29152          * @event clientvalidation
29153          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29154          * @param {Form} this
29155          * @param {Boolean} valid true if the form has passed client-side validation
29156          */
29157         clientvalidation: true,
29158         /**
29159          * @event rendered
29160          * Fires when the form is rendered
29161          * @param {Roo.form.Form} form
29162          */
29163         rendered : true
29164     });
29165     
29166     if (this.progressUrl) {
29167             // push a hidden field onto the list of fields..
29168             this.addxtype( {
29169                     xns: Roo.form, 
29170                     xtype : 'Hidden', 
29171                     name : 'UPLOAD_IDENTIFIER' 
29172             });
29173         }
29174         
29175     
29176     Roo.each(xitems, this.addxtype, this);
29177     
29178     
29179     
29180 };
29181
29182 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29183     /**
29184      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29185      */
29186     /**
29187      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29188      */
29189     /**
29190      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29191      */
29192     buttonAlign:'center',
29193
29194     /**
29195      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29196      */
29197     minButtonWidth:75,
29198
29199     /**
29200      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29201      * This property cascades to child containers if not set.
29202      */
29203     labelAlign:'left',
29204
29205     /**
29206      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29207      * fires a looping event with that state. This is required to bind buttons to the valid
29208      * state using the config value formBind:true on the button.
29209      */
29210     monitorValid : false,
29211
29212     /**
29213      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29214      */
29215     monitorPoll : 200,
29216     
29217     /**
29218      * @cfg {String} progressUrl - Url to return progress data 
29219      */
29220     
29221     progressUrl : false,
29222   
29223     /**
29224      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29225      * fields are added and the column is closed. If no fields are passed the column remains open
29226      * until end() is called.
29227      * @param {Object} config The config to pass to the column
29228      * @param {Field} field1 (optional)
29229      * @param {Field} field2 (optional)
29230      * @param {Field} etc (optional)
29231      * @return Column The column container object
29232      */
29233     column : function(c){
29234         var col = new Roo.form.Column(c);
29235         this.start(col);
29236         if(arguments.length > 1){ // duplicate code required because of Opera
29237             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29238             this.end();
29239         }
29240         return col;
29241     },
29242
29243     /**
29244      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29245      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29246      * until end() is called.
29247      * @param {Object} config The config to pass to the fieldset
29248      * @param {Field} field1 (optional)
29249      * @param {Field} field2 (optional)
29250      * @param {Field} etc (optional)
29251      * @return FieldSet The fieldset container object
29252      */
29253     fieldset : function(c){
29254         var fs = new Roo.form.FieldSet(c);
29255         this.start(fs);
29256         if(arguments.length > 1){ // duplicate code required because of Opera
29257             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29258             this.end();
29259         }
29260         return fs;
29261     },
29262
29263     /**
29264      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29265      * fields are added and the container is closed. If no fields are passed the container remains open
29266      * until end() is called.
29267      * @param {Object} config The config to pass to the Layout
29268      * @param {Field} field1 (optional)
29269      * @param {Field} field2 (optional)
29270      * @param {Field} etc (optional)
29271      * @return Layout The container object
29272      */
29273     container : function(c){
29274         var l = new Roo.form.Layout(c);
29275         this.start(l);
29276         if(arguments.length > 1){ // duplicate code required because of Opera
29277             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29278             this.end();
29279         }
29280         return l;
29281     },
29282
29283     /**
29284      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29285      * @param {Object} container A Roo.form.Layout or subclass of Layout
29286      * @return {Form} this
29287      */
29288     start : function(c){
29289         // cascade label info
29290         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29291         this.active.stack.push(c);
29292         c.ownerCt = this.active;
29293         this.active = c;
29294         return this;
29295     },
29296
29297     /**
29298      * Closes the current open container
29299      * @return {Form} this
29300      */
29301     end : function(){
29302         if(this.active == this.root){
29303             return this;
29304         }
29305         this.active = this.active.ownerCt;
29306         return this;
29307     },
29308
29309     /**
29310      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29311      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29312      * as the label of the field.
29313      * @param {Field} field1
29314      * @param {Field} field2 (optional)
29315      * @param {Field} etc. (optional)
29316      * @return {Form} this
29317      */
29318     add : function(){
29319         this.active.stack.push.apply(this.active.stack, arguments);
29320         this.allItems.push.apply(this.allItems,arguments);
29321         var r = [];
29322         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29323             if(a[i].isFormField){
29324                 r.push(a[i]);
29325             }
29326         }
29327         if(r.length > 0){
29328             Roo.form.Form.superclass.add.apply(this, r);
29329         }
29330         return this;
29331     },
29332     
29333
29334     
29335     
29336     
29337      /**
29338      * Find any element that has been added to a form, using it's ID or name
29339      * This can include framesets, columns etc. along with regular fields..
29340      * @param {String} id - id or name to find.
29341      
29342      * @return {Element} e - or false if nothing found.
29343      */
29344     findbyId : function(id)
29345     {
29346         var ret = false;
29347         if (!id) {
29348             return ret;
29349         }
29350         Roo.each(this.allItems, function(f){
29351             if (f.id == id || f.name == id ){
29352                 ret = f;
29353                 return false;
29354             }
29355         });
29356         return ret;
29357     },
29358
29359     
29360     
29361     /**
29362      * Render this form into the passed container. This should only be called once!
29363      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29364      * @return {Form} this
29365      */
29366     render : function(ct)
29367     {
29368         
29369         
29370         
29371         ct = Roo.get(ct);
29372         var o = this.autoCreate || {
29373             tag: 'form',
29374             method : this.method || 'POST',
29375             id : this.id || Roo.id()
29376         };
29377         this.initEl(ct.createChild(o));
29378
29379         this.root.render(this.el);
29380         
29381        
29382              
29383         this.items.each(function(f){
29384             f.render('x-form-el-'+f.id);
29385         });
29386
29387         if(this.buttons.length > 0){
29388             // tables are required to maintain order and for correct IE layout
29389             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29390                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29391                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29392             }}, null, true);
29393             var tr = tb.getElementsByTagName('tr')[0];
29394             for(var i = 0, len = this.buttons.length; i < len; i++) {
29395                 var b = this.buttons[i];
29396                 var td = document.createElement('td');
29397                 td.className = 'x-form-btn-td';
29398                 b.render(tr.appendChild(td));
29399             }
29400         }
29401         if(this.monitorValid){ // initialize after render
29402             this.startMonitoring();
29403         }
29404         this.fireEvent('rendered', this);
29405         return this;
29406     },
29407
29408     /**
29409      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29410      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29411      * object or a valid Roo.DomHelper element config
29412      * @param {Function} handler The function called when the button is clicked
29413      * @param {Object} scope (optional) The scope of the handler function
29414      * @return {Roo.Button}
29415      */
29416     addButton : function(config, handler, scope){
29417         var bc = {
29418             handler: handler,
29419             scope: scope,
29420             minWidth: this.minButtonWidth,
29421             hideParent:true
29422         };
29423         if(typeof config == "string"){
29424             bc.text = config;
29425         }else{
29426             Roo.apply(bc, config);
29427         }
29428         var btn = new Roo.Button(null, bc);
29429         this.buttons.push(btn);
29430         return btn;
29431     },
29432
29433      /**
29434      * Adds a series of form elements (using the xtype property as the factory method.
29435      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29436      * @param {Object} config 
29437      */
29438     
29439     addxtype : function()
29440     {
29441         var ar = Array.prototype.slice.call(arguments, 0);
29442         var ret = false;
29443         for(var i = 0; i < ar.length; i++) {
29444             if (!ar[i]) {
29445                 continue; // skip -- if this happends something invalid got sent, we 
29446                 // should ignore it, as basically that interface element will not show up
29447                 // and that should be pretty obvious!!
29448             }
29449             
29450             if (Roo.form[ar[i].xtype]) {
29451                 ar[i].form = this;
29452                 var fe = Roo.factory(ar[i], Roo.form);
29453                 if (!ret) {
29454                     ret = fe;
29455                 }
29456                 fe.form = this;
29457                 if (fe.store) {
29458                     fe.store.form = this;
29459                 }
29460                 if (fe.isLayout) {  
29461                          
29462                     this.start(fe);
29463                     this.allItems.push(fe);
29464                     if (fe.items && fe.addxtype) {
29465                         fe.addxtype.apply(fe, fe.items);
29466                         delete fe.items;
29467                     }
29468                      this.end();
29469                     continue;
29470                 }
29471                 
29472                 
29473                  
29474                 this.add(fe);
29475               //  console.log('adding ' + ar[i].xtype);
29476             }
29477             if (ar[i].xtype == 'Button') {  
29478                 //console.log('adding button');
29479                 //console.log(ar[i]);
29480                 this.addButton(ar[i]);
29481                 this.allItems.push(fe);
29482                 continue;
29483             }
29484             
29485             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29486                 alert('end is not supported on xtype any more, use items');
29487             //    this.end();
29488             //    //console.log('adding end');
29489             }
29490             
29491         }
29492         return ret;
29493     },
29494     
29495     /**
29496      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29497      * option "monitorValid"
29498      */
29499     startMonitoring : function(){
29500         if(!this.bound){
29501             this.bound = true;
29502             Roo.TaskMgr.start({
29503                 run : this.bindHandler,
29504                 interval : this.monitorPoll || 200,
29505                 scope: this
29506             });
29507         }
29508     },
29509
29510     /**
29511      * Stops monitoring of the valid state of this form
29512      */
29513     stopMonitoring : function(){
29514         this.bound = false;
29515     },
29516
29517     // private
29518     bindHandler : function(){
29519         if(!this.bound){
29520             return false; // stops binding
29521         }
29522         var valid = true;
29523         this.items.each(function(f){
29524             if(!f.isValid(true)){
29525                 valid = false;
29526                 return false;
29527             }
29528         });
29529         for(var i = 0, len = this.buttons.length; i < len; i++){
29530             var btn = this.buttons[i];
29531             if(btn.formBind === true && btn.disabled === valid){
29532                 btn.setDisabled(!valid);
29533             }
29534         }
29535         this.fireEvent('clientvalidation', this, valid);
29536     }
29537     
29538     
29539     
29540     
29541     
29542     
29543     
29544     
29545 });
29546
29547
29548 // back compat
29549 Roo.Form = Roo.form.Form;
29550 /*
29551  * Based on:
29552  * Ext JS Library 1.1.1
29553  * Copyright(c) 2006-2007, Ext JS, LLC.
29554  *
29555  * Originally Released Under LGPL - original licence link has changed is not relivant.
29556  *
29557  * Fork - LGPL
29558  * <script type="text/javascript">
29559  */
29560
29561 // as we use this in bootstrap.
29562 Roo.namespace('Roo.form');
29563  /**
29564  * @class Roo.form.Action
29565  * Internal Class used to handle form actions
29566  * @constructor
29567  * @param {Roo.form.BasicForm} el The form element or its id
29568  * @param {Object} config Configuration options
29569  */
29570
29571  
29572  
29573 // define the action interface
29574 Roo.form.Action = function(form, options){
29575     this.form = form;
29576     this.options = options || {};
29577 };
29578 /**
29579  * Client Validation Failed
29580  * @const 
29581  */
29582 Roo.form.Action.CLIENT_INVALID = 'client';
29583 /**
29584  * Server Validation Failed
29585  * @const 
29586  */
29587 Roo.form.Action.SERVER_INVALID = 'server';
29588  /**
29589  * Connect to Server Failed
29590  * @const 
29591  */
29592 Roo.form.Action.CONNECT_FAILURE = 'connect';
29593 /**
29594  * Reading Data from Server Failed
29595  * @const 
29596  */
29597 Roo.form.Action.LOAD_FAILURE = 'load';
29598
29599 Roo.form.Action.prototype = {
29600     type : 'default',
29601     failureType : undefined,
29602     response : undefined,
29603     result : undefined,
29604
29605     // interface method
29606     run : function(options){
29607
29608     },
29609
29610     // interface method
29611     success : function(response){
29612
29613     },
29614
29615     // interface method
29616     handleResponse : function(response){
29617
29618     },
29619
29620     // default connection failure
29621     failure : function(response){
29622         
29623         this.response = response;
29624         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29625         this.form.afterAction(this, false);
29626     },
29627
29628     processResponse : function(response){
29629         this.response = response;
29630         if(!response.responseText){
29631             return true;
29632         }
29633         this.result = this.handleResponse(response);
29634         return this.result;
29635     },
29636
29637     // utility functions used internally
29638     getUrl : function(appendParams){
29639         var url = this.options.url || this.form.url || this.form.el.dom.action;
29640         if(appendParams){
29641             var p = this.getParams();
29642             if(p){
29643                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29644             }
29645         }
29646         return url;
29647     },
29648
29649     getMethod : function(){
29650         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29651     },
29652
29653     getParams : function(){
29654         var bp = this.form.baseParams;
29655         var p = this.options.params;
29656         if(p){
29657             if(typeof p == "object"){
29658                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29659             }else if(typeof p == 'string' && bp){
29660                 p += '&' + Roo.urlEncode(bp);
29661             }
29662         }else if(bp){
29663             p = Roo.urlEncode(bp);
29664         }
29665         return p;
29666     },
29667
29668     createCallback : function(){
29669         return {
29670             success: this.success,
29671             failure: this.failure,
29672             scope: this,
29673             timeout: (this.form.timeout*1000),
29674             upload: this.form.fileUpload ? this.success : undefined
29675         };
29676     }
29677 };
29678
29679 Roo.form.Action.Submit = function(form, options){
29680     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29681 };
29682
29683 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29684     type : 'submit',
29685
29686     haveProgress : false,
29687     uploadComplete : false,
29688     
29689     // uploadProgress indicator.
29690     uploadProgress : function()
29691     {
29692         if (!this.form.progressUrl) {
29693             return;
29694         }
29695         
29696         if (!this.haveProgress) {
29697             Roo.MessageBox.progress("Uploading", "Uploading");
29698         }
29699         if (this.uploadComplete) {
29700            Roo.MessageBox.hide();
29701            return;
29702         }
29703         
29704         this.haveProgress = true;
29705    
29706         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29707         
29708         var c = new Roo.data.Connection();
29709         c.request({
29710             url : this.form.progressUrl,
29711             params: {
29712                 id : uid
29713             },
29714             method: 'GET',
29715             success : function(req){
29716                //console.log(data);
29717                 var rdata = false;
29718                 var edata;
29719                 try  {
29720                    rdata = Roo.decode(req.responseText)
29721                 } catch (e) {
29722                     Roo.log("Invalid data from server..");
29723                     Roo.log(edata);
29724                     return;
29725                 }
29726                 if (!rdata || !rdata.success) {
29727                     Roo.log(rdata);
29728                     Roo.MessageBox.alert(Roo.encode(rdata));
29729                     return;
29730                 }
29731                 var data = rdata.data;
29732                 
29733                 if (this.uploadComplete) {
29734                    Roo.MessageBox.hide();
29735                    return;
29736                 }
29737                    
29738                 if (data){
29739                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29740                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29741                     );
29742                 }
29743                 this.uploadProgress.defer(2000,this);
29744             },
29745        
29746             failure: function(data) {
29747                 Roo.log('progress url failed ');
29748                 Roo.log(data);
29749             },
29750             scope : this
29751         });
29752            
29753     },
29754     
29755     
29756     run : function()
29757     {
29758         // run get Values on the form, so it syncs any secondary forms.
29759         this.form.getValues();
29760         
29761         var o = this.options;
29762         var method = this.getMethod();
29763         var isPost = method == 'POST';
29764         if(o.clientValidation === false || this.form.isValid()){
29765             
29766             if (this.form.progressUrl) {
29767                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29768                     (new Date() * 1) + '' + Math.random());
29769                     
29770             } 
29771             
29772             
29773             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29774                 form:this.form.el.dom,
29775                 url:this.getUrl(!isPost),
29776                 method: method,
29777                 params:isPost ? this.getParams() : null,
29778                 isUpload: this.form.fileUpload
29779             }));
29780             
29781             this.uploadProgress();
29782
29783         }else if (o.clientValidation !== false){ // client validation failed
29784             this.failureType = Roo.form.Action.CLIENT_INVALID;
29785             this.form.afterAction(this, false);
29786         }
29787     },
29788
29789     success : function(response)
29790     {
29791         this.uploadComplete= true;
29792         if (this.haveProgress) {
29793             Roo.MessageBox.hide();
29794         }
29795         
29796         
29797         var result = this.processResponse(response);
29798         if(result === true || result.success){
29799             this.form.afterAction(this, true);
29800             return;
29801         }
29802         if(result.errors){
29803             this.form.markInvalid(result.errors);
29804             this.failureType = Roo.form.Action.SERVER_INVALID;
29805         }
29806         this.form.afterAction(this, false);
29807     },
29808     failure : function(response)
29809     {
29810         this.uploadComplete= true;
29811         if (this.haveProgress) {
29812             Roo.MessageBox.hide();
29813         }
29814         
29815         this.response = response;
29816         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29817         this.form.afterAction(this, false);
29818     },
29819     
29820     handleResponse : function(response){
29821         if(this.form.errorReader){
29822             var rs = this.form.errorReader.read(response);
29823             var errors = [];
29824             if(rs.records){
29825                 for(var i = 0, len = rs.records.length; i < len; i++) {
29826                     var r = rs.records[i];
29827                     errors[i] = r.data;
29828                 }
29829             }
29830             if(errors.length < 1){
29831                 errors = null;
29832             }
29833             return {
29834                 success : rs.success,
29835                 errors : errors
29836             };
29837         }
29838         var ret = false;
29839         try {
29840             ret = Roo.decode(response.responseText);
29841         } catch (e) {
29842             ret = {
29843                 success: false,
29844                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29845                 errors : []
29846             };
29847         }
29848         return ret;
29849         
29850     }
29851 });
29852
29853
29854 Roo.form.Action.Load = function(form, options){
29855     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29856     this.reader = this.form.reader;
29857 };
29858
29859 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29860     type : 'load',
29861
29862     run : function(){
29863         
29864         Roo.Ajax.request(Roo.apply(
29865                 this.createCallback(), {
29866                     method:this.getMethod(),
29867                     url:this.getUrl(false),
29868                     params:this.getParams()
29869         }));
29870     },
29871
29872     success : function(response){
29873         
29874         var result = this.processResponse(response);
29875         if(result === true || !result.success || !result.data){
29876             this.failureType = Roo.form.Action.LOAD_FAILURE;
29877             this.form.afterAction(this, false);
29878             return;
29879         }
29880         this.form.clearInvalid();
29881         this.form.setValues(result.data);
29882         this.form.afterAction(this, true);
29883     },
29884
29885     handleResponse : function(response){
29886         if(this.form.reader){
29887             var rs = this.form.reader.read(response);
29888             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29889             return {
29890                 success : rs.success,
29891                 data : data
29892             };
29893         }
29894         return Roo.decode(response.responseText);
29895     }
29896 });
29897
29898 Roo.form.Action.ACTION_TYPES = {
29899     'load' : Roo.form.Action.Load,
29900     'submit' : Roo.form.Action.Submit
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 /**
29913  * @class Roo.form.Layout
29914  * @extends Roo.Component
29915  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29916  * @constructor
29917  * @param {Object} config Configuration options
29918  */
29919 Roo.form.Layout = function(config){
29920     var xitems = [];
29921     if (config.items) {
29922         xitems = config.items;
29923         delete config.items;
29924     }
29925     Roo.form.Layout.superclass.constructor.call(this, config);
29926     this.stack = [];
29927     Roo.each(xitems, this.addxtype, this);
29928      
29929 };
29930
29931 Roo.extend(Roo.form.Layout, Roo.Component, {
29932     /**
29933      * @cfg {String/Object} autoCreate
29934      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29935      */
29936     /**
29937      * @cfg {String/Object/Function} style
29938      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29939      * a function which returns such a specification.
29940      */
29941     /**
29942      * @cfg {String} labelAlign
29943      * Valid values are "left," "top" and "right" (defaults to "left")
29944      */
29945     /**
29946      * @cfg {Number} labelWidth
29947      * Fixed width in pixels of all field labels (defaults to undefined)
29948      */
29949     /**
29950      * @cfg {Boolean} clear
29951      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29952      */
29953     clear : true,
29954     /**
29955      * @cfg {String} labelSeparator
29956      * The separator to use after field labels (defaults to ':')
29957      */
29958     labelSeparator : ':',
29959     /**
29960      * @cfg {Boolean} hideLabels
29961      * True to suppress the display of field labels in this layout (defaults to false)
29962      */
29963     hideLabels : false,
29964
29965     // private
29966     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29967     
29968     isLayout : true,
29969     
29970     // private
29971     onRender : function(ct, position){
29972         if(this.el){ // from markup
29973             this.el = Roo.get(this.el);
29974         }else {  // generate
29975             var cfg = this.getAutoCreate();
29976             this.el = ct.createChild(cfg, position);
29977         }
29978         if(this.style){
29979             this.el.applyStyles(this.style);
29980         }
29981         if(this.labelAlign){
29982             this.el.addClass('x-form-label-'+this.labelAlign);
29983         }
29984         if(this.hideLabels){
29985             this.labelStyle = "display:none";
29986             this.elementStyle = "padding-left:0;";
29987         }else{
29988             if(typeof this.labelWidth == 'number'){
29989                 this.labelStyle = "width:"+this.labelWidth+"px;";
29990                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29991             }
29992             if(this.labelAlign == 'top'){
29993                 this.labelStyle = "width:auto;";
29994                 this.elementStyle = "padding-left:0;";
29995             }
29996         }
29997         var stack = this.stack;
29998         var slen = stack.length;
29999         if(slen > 0){
30000             if(!this.fieldTpl){
30001                 var t = new Roo.Template(
30002                     '<div class="x-form-item {5}">',
30003                         '<label for="{0}" style="{2}">{1}{4}</label>',
30004                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30005                         '</div>',
30006                     '</div><div class="x-form-clear-left"></div>'
30007                 );
30008                 t.disableFormats = true;
30009                 t.compile();
30010                 Roo.form.Layout.prototype.fieldTpl = t;
30011             }
30012             for(var i = 0; i < slen; i++) {
30013                 if(stack[i].isFormField){
30014                     this.renderField(stack[i]);
30015                 }else{
30016                     this.renderComponent(stack[i]);
30017                 }
30018             }
30019         }
30020         if(this.clear){
30021             this.el.createChild({cls:'x-form-clear'});
30022         }
30023     },
30024
30025     // private
30026     renderField : function(f){
30027         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30028                f.id, //0
30029                f.fieldLabel, //1
30030                f.labelStyle||this.labelStyle||'', //2
30031                this.elementStyle||'', //3
30032                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30033                f.itemCls||this.itemCls||''  //5
30034        ], true).getPrevSibling());
30035     },
30036
30037     // private
30038     renderComponent : function(c){
30039         c.render(c.isLayout ? this.el : this.el.createChild());    
30040     },
30041     /**
30042      * Adds a object form elements (using the xtype property as the factory method.)
30043      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30044      * @param {Object} config 
30045      */
30046     addxtype : function(o)
30047     {
30048         // create the lement.
30049         o.form = this.form;
30050         var fe = Roo.factory(o, Roo.form);
30051         this.form.allItems.push(fe);
30052         this.stack.push(fe);
30053         
30054         if (fe.isFormField) {
30055             this.form.items.add(fe);
30056         }
30057          
30058         return fe;
30059     }
30060 });
30061
30062 /**
30063  * @class Roo.form.Column
30064  * @extends Roo.form.Layout
30065  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30066  * @constructor
30067  * @param {Object} config Configuration options
30068  */
30069 Roo.form.Column = function(config){
30070     Roo.form.Column.superclass.constructor.call(this, config);
30071 };
30072
30073 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30074     /**
30075      * @cfg {Number/String} width
30076      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30077      */
30078     /**
30079      * @cfg {String/Object} autoCreate
30080      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30081      */
30082
30083     // private
30084     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30085
30086     // private
30087     onRender : function(ct, position){
30088         Roo.form.Column.superclass.onRender.call(this, ct, position);
30089         if(this.width){
30090             this.el.setWidth(this.width);
30091         }
30092     }
30093 });
30094
30095
30096 /**
30097  * @class Roo.form.Row
30098  * @extends Roo.form.Layout
30099  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30100  * @constructor
30101  * @param {Object} config Configuration options
30102  */
30103
30104  
30105 Roo.form.Row = function(config){
30106     Roo.form.Row.superclass.constructor.call(this, config);
30107 };
30108  
30109 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30110       /**
30111      * @cfg {Number/String} width
30112      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30113      */
30114     /**
30115      * @cfg {Number/String} height
30116      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30117      */
30118     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30119     
30120     padWidth : 20,
30121     // private
30122     onRender : function(ct, position){
30123         //console.log('row render');
30124         if(!this.rowTpl){
30125             var t = new Roo.Template(
30126                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30127                     '<label for="{0}" style="{2}">{1}{4}</label>',
30128                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30129                     '</div>',
30130                 '</div>'
30131             );
30132             t.disableFormats = true;
30133             t.compile();
30134             Roo.form.Layout.prototype.rowTpl = t;
30135         }
30136         this.fieldTpl = this.rowTpl;
30137         
30138         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30139         var labelWidth = 100;
30140         
30141         if ((this.labelAlign != 'top')) {
30142             if (typeof this.labelWidth == 'number') {
30143                 labelWidth = this.labelWidth
30144             }
30145             this.padWidth =  20 + labelWidth;
30146             
30147         }
30148         
30149         Roo.form.Column.superclass.onRender.call(this, ct, position);
30150         if(this.width){
30151             this.el.setWidth(this.width);
30152         }
30153         if(this.height){
30154             this.el.setHeight(this.height);
30155         }
30156     },
30157     
30158     // private
30159     renderField : function(f){
30160         f.fieldEl = this.fieldTpl.append(this.el, [
30161                f.id, f.fieldLabel,
30162                f.labelStyle||this.labelStyle||'',
30163                this.elementStyle||'',
30164                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30165                f.itemCls||this.itemCls||'',
30166                f.width ? f.width + this.padWidth : 160 + this.padWidth
30167        ],true);
30168     }
30169 });
30170  
30171
30172 /**
30173  * @class Roo.form.FieldSet
30174  * @extends Roo.form.Layout
30175  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30176  * @constructor
30177  * @param {Object} config Configuration options
30178  */
30179 Roo.form.FieldSet = function(config){
30180     Roo.form.FieldSet.superclass.constructor.call(this, config);
30181 };
30182
30183 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30184     /**
30185      * @cfg {String} legend
30186      * The text to display as the legend for the FieldSet (defaults to '')
30187      */
30188     /**
30189      * @cfg {String/Object} autoCreate
30190      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30191      */
30192
30193     // private
30194     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30195
30196     // private
30197     onRender : function(ct, position){
30198         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30199         if(this.legend){
30200             this.setLegend(this.legend);
30201         }
30202     },
30203
30204     // private
30205     setLegend : function(text){
30206         if(this.rendered){
30207             this.el.child('legend').update(text);
30208         }
30209     }
30210 });/*
30211  * Based on:
30212  * Ext JS Library 1.1.1
30213  * Copyright(c) 2006-2007, Ext JS, LLC.
30214  *
30215  * Originally Released Under LGPL - original licence link has changed is not relivant.
30216  *
30217  * Fork - LGPL
30218  * <script type="text/javascript">
30219  */
30220 /**
30221  * @class Roo.form.VTypes
30222  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30223  * @singleton
30224  */
30225 Roo.form.VTypes = function(){
30226     // closure these in so they are only created once.
30227     var alpha = /^[a-zA-Z_]+$/;
30228     var alphanum = /^[a-zA-Z0-9_]+$/;
30229     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30230     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30231
30232     // All these messages and functions are configurable
30233     return {
30234         /**
30235          * The function used to validate email addresses
30236          * @param {String} value The email address
30237          */
30238         'email' : function(v){
30239             return email.test(v);
30240         },
30241         /**
30242          * The error text to display when the email validation function returns false
30243          * @type String
30244          */
30245         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30246         /**
30247          * The keystroke filter mask to be applied on email input
30248          * @type RegExp
30249          */
30250         'emailMask' : /[a-z0-9_\.\-@]/i,
30251
30252         /**
30253          * The function used to validate URLs
30254          * @param {String} value The URL
30255          */
30256         'url' : function(v){
30257             return url.test(v);
30258         },
30259         /**
30260          * The error text to display when the url validation function returns false
30261          * @type String
30262          */
30263         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30264         
30265         /**
30266          * The function used to validate alpha values
30267          * @param {String} value The value
30268          */
30269         'alpha' : function(v){
30270             return alpha.test(v);
30271         },
30272         /**
30273          * The error text to display when the alpha validation function returns false
30274          * @type String
30275          */
30276         'alphaText' : 'This field should only contain letters and _',
30277         /**
30278          * The keystroke filter mask to be applied on alpha input
30279          * @type RegExp
30280          */
30281         'alphaMask' : /[a-z_]/i,
30282
30283         /**
30284          * The function used to validate alphanumeric values
30285          * @param {String} value The value
30286          */
30287         'alphanum' : function(v){
30288             return alphanum.test(v);
30289         },
30290         /**
30291          * The error text to display when the alphanumeric validation function returns false
30292          * @type String
30293          */
30294         'alphanumText' : 'This field should only contain letters, numbers and _',
30295         /**
30296          * The keystroke filter mask to be applied on alphanumeric input
30297          * @type RegExp
30298          */
30299         'alphanumMask' : /[a-z0-9_]/i
30300     };
30301 }();//<script type="text/javascript">
30302
30303 /**
30304  * @class Roo.form.FCKeditor
30305  * @extends Roo.form.TextArea
30306  * Wrapper around the FCKEditor http://www.fckeditor.net
30307  * @constructor
30308  * Creates a new FCKeditor
30309  * @param {Object} config Configuration options
30310  */
30311 Roo.form.FCKeditor = function(config){
30312     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30313     this.addEvents({
30314          /**
30315          * @event editorinit
30316          * Fired when the editor is initialized - you can add extra handlers here..
30317          * @param {FCKeditor} this
30318          * @param {Object} the FCK object.
30319          */
30320         editorinit : true
30321     });
30322     
30323     
30324 };
30325 Roo.form.FCKeditor.editors = { };
30326 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30327 {
30328     //defaultAutoCreate : {
30329     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30330     //},
30331     // private
30332     /**
30333      * @cfg {Object} fck options - see fck manual for details.
30334      */
30335     fckconfig : false,
30336     
30337     /**
30338      * @cfg {Object} fck toolbar set (Basic or Default)
30339      */
30340     toolbarSet : 'Basic',
30341     /**
30342      * @cfg {Object} fck BasePath
30343      */ 
30344     basePath : '/fckeditor/',
30345     
30346     
30347     frame : false,
30348     
30349     value : '',
30350     
30351    
30352     onRender : function(ct, position)
30353     {
30354         if(!this.el){
30355             this.defaultAutoCreate = {
30356                 tag: "textarea",
30357                 style:"width:300px;height:60px;",
30358                 autocomplete: "new-password"
30359             };
30360         }
30361         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30362         /*
30363         if(this.grow){
30364             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30365             if(this.preventScrollbars){
30366                 this.el.setStyle("overflow", "hidden");
30367             }
30368             this.el.setHeight(this.growMin);
30369         }
30370         */
30371         //console.log('onrender' + this.getId() );
30372         Roo.form.FCKeditor.editors[this.getId()] = this;
30373          
30374
30375         this.replaceTextarea() ;
30376         
30377     },
30378     
30379     getEditor : function() {
30380         return this.fckEditor;
30381     },
30382     /**
30383      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30384      * @param {Mixed} value The value to set
30385      */
30386     
30387     
30388     setValue : function(value)
30389     {
30390         //console.log('setValue: ' + value);
30391         
30392         if(typeof(value) == 'undefined') { // not sure why this is happending...
30393             return;
30394         }
30395         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30396         
30397         //if(!this.el || !this.getEditor()) {
30398         //    this.value = value;
30399             //this.setValue.defer(100,this,[value]);    
30400         //    return;
30401         //} 
30402         
30403         if(!this.getEditor()) {
30404             return;
30405         }
30406         
30407         this.getEditor().SetData(value);
30408         
30409         //
30410
30411     },
30412
30413     /**
30414      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30415      * @return {Mixed} value The field value
30416      */
30417     getValue : function()
30418     {
30419         
30420         if (this.frame && this.frame.dom.style.display == 'none') {
30421             return Roo.form.FCKeditor.superclass.getValue.call(this);
30422         }
30423         
30424         if(!this.el || !this.getEditor()) {
30425            
30426            // this.getValue.defer(100,this); 
30427             return this.value;
30428         }
30429        
30430         
30431         var value=this.getEditor().GetData();
30432         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30433         return Roo.form.FCKeditor.superclass.getValue.call(this);
30434         
30435
30436     },
30437
30438     /**
30439      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30440      * @return {Mixed} value The field value
30441      */
30442     getRawValue : function()
30443     {
30444         if (this.frame && this.frame.dom.style.display == 'none') {
30445             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30446         }
30447         
30448         if(!this.el || !this.getEditor()) {
30449             //this.getRawValue.defer(100,this); 
30450             return this.value;
30451             return;
30452         }
30453         
30454         
30455         
30456         var value=this.getEditor().GetData();
30457         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30458         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30459          
30460     },
30461     
30462     setSize : function(w,h) {
30463         
30464         
30465         
30466         //if (this.frame && this.frame.dom.style.display == 'none') {
30467         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30468         //    return;
30469         //}
30470         //if(!this.el || !this.getEditor()) {
30471         //    this.setSize.defer(100,this, [w,h]); 
30472         //    return;
30473         //}
30474         
30475         
30476         
30477         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30478         
30479         this.frame.dom.setAttribute('width', w);
30480         this.frame.dom.setAttribute('height', h);
30481         this.frame.setSize(w,h);
30482         
30483     },
30484     
30485     toggleSourceEdit : function(value) {
30486         
30487       
30488          
30489         this.el.dom.style.display = value ? '' : 'none';
30490         this.frame.dom.style.display = value ?  'none' : '';
30491         
30492     },
30493     
30494     
30495     focus: function(tag)
30496     {
30497         if (this.frame.dom.style.display == 'none') {
30498             return Roo.form.FCKeditor.superclass.focus.call(this);
30499         }
30500         if(!this.el || !this.getEditor()) {
30501             this.focus.defer(100,this, [tag]); 
30502             return;
30503         }
30504         
30505         
30506         
30507         
30508         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30509         this.getEditor().Focus();
30510         if (tgs.length) {
30511             if (!this.getEditor().Selection.GetSelection()) {
30512                 this.focus.defer(100,this, [tag]); 
30513                 return;
30514             }
30515             
30516             
30517             var r = this.getEditor().EditorDocument.createRange();
30518             r.setStart(tgs[0],0);
30519             r.setEnd(tgs[0],0);
30520             this.getEditor().Selection.GetSelection().removeAllRanges();
30521             this.getEditor().Selection.GetSelection().addRange(r);
30522             this.getEditor().Focus();
30523         }
30524         
30525     },
30526     
30527     
30528     
30529     replaceTextarea : function()
30530     {
30531         if ( document.getElementById( this.getId() + '___Frame' ) )
30532             return ;
30533         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30534         //{
30535             // We must check the elements firstly using the Id and then the name.
30536         var oTextarea = document.getElementById( this.getId() );
30537         
30538         var colElementsByName = document.getElementsByName( this.getId() ) ;
30539          
30540         oTextarea.style.display = 'none' ;
30541
30542         if ( oTextarea.tabIndex ) {            
30543             this.TabIndex = oTextarea.tabIndex ;
30544         }
30545         
30546         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30547         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30548         this.frame = Roo.get(this.getId() + '___Frame')
30549     },
30550     
30551     _getConfigHtml : function()
30552     {
30553         var sConfig = '' ;
30554
30555         for ( var o in this.fckconfig ) {
30556             sConfig += sConfig.length > 0  ? '&amp;' : '';
30557             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30558         }
30559
30560         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30561     },
30562     
30563     
30564     _getIFrameHtml : function()
30565     {
30566         var sFile = 'fckeditor.html' ;
30567         /* no idea what this is about..
30568         try
30569         {
30570             if ( (/fcksource=true/i).test( window.top.location.search ) )
30571                 sFile = 'fckeditor.original.html' ;
30572         }
30573         catch (e) { 
30574         */
30575
30576         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30577         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30578         
30579         
30580         var html = '<iframe id="' + this.getId() +
30581             '___Frame" src="' + sLink +
30582             '" width="' + this.width +
30583             '" height="' + this.height + '"' +
30584             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30585             ' frameborder="0" scrolling="no"></iframe>' ;
30586
30587         return html ;
30588     },
30589     
30590     _insertHtmlBefore : function( html, element )
30591     {
30592         if ( element.insertAdjacentHTML )       {
30593             // IE
30594             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30595         } else { // Gecko
30596             var oRange = document.createRange() ;
30597             oRange.setStartBefore( element ) ;
30598             var oFragment = oRange.createContextualFragment( html );
30599             element.parentNode.insertBefore( oFragment, element ) ;
30600         }
30601     }
30602     
30603     
30604   
30605     
30606     
30607     
30608     
30609
30610 });
30611
30612 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30613
30614 function FCKeditor_OnComplete(editorInstance){
30615     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30616     f.fckEditor = editorInstance;
30617     //console.log("loaded");
30618     f.fireEvent('editorinit', f, editorInstance);
30619
30620   
30621
30622  
30623
30624
30625
30626
30627
30628
30629
30630
30631
30632
30633
30634
30635
30636
30637
30638 //<script type="text/javascript">
30639 /**
30640  * @class Roo.form.GridField
30641  * @extends Roo.form.Field
30642  * Embed a grid (or editable grid into a form)
30643  * STATUS ALPHA
30644  * 
30645  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30646  * it needs 
30647  * xgrid.store = Roo.data.Store
30648  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30649  * xgrid.store.reader = Roo.data.JsonReader 
30650  * 
30651  * 
30652  * @constructor
30653  * Creates a new GridField
30654  * @param {Object} config Configuration options
30655  */
30656 Roo.form.GridField = function(config){
30657     Roo.form.GridField.superclass.constructor.call(this, config);
30658      
30659 };
30660
30661 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30662     /**
30663      * @cfg {Number} width  - used to restrict width of grid..
30664      */
30665     width : 100,
30666     /**
30667      * @cfg {Number} height - used to restrict height of grid..
30668      */
30669     height : 50,
30670      /**
30671      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30672          * 
30673          *}
30674      */
30675     xgrid : false, 
30676     /**
30677      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30678      * {tag: "input", type: "checkbox", autocomplete: "off"})
30679      */
30680    // defaultAutoCreate : { tag: 'div' },
30681     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30682     /**
30683      * @cfg {String} addTitle Text to include for adding a title.
30684      */
30685     addTitle : false,
30686     //
30687     onResize : function(){
30688         Roo.form.Field.superclass.onResize.apply(this, arguments);
30689     },
30690
30691     initEvents : function(){
30692         // Roo.form.Checkbox.superclass.initEvents.call(this);
30693         // has no events...
30694        
30695     },
30696
30697
30698     getResizeEl : function(){
30699         return this.wrap;
30700     },
30701
30702     getPositionEl : function(){
30703         return this.wrap;
30704     },
30705
30706     // private
30707     onRender : function(ct, position){
30708         
30709         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30710         var style = this.style;
30711         delete this.style;
30712         
30713         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30714         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30715         this.viewEl = this.wrap.createChild({ tag: 'div' });
30716         if (style) {
30717             this.viewEl.applyStyles(style);
30718         }
30719         if (this.width) {
30720             this.viewEl.setWidth(this.width);
30721         }
30722         if (this.height) {
30723             this.viewEl.setHeight(this.height);
30724         }
30725         //if(this.inputValue !== undefined){
30726         //this.setValue(this.value);
30727         
30728         
30729         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30730         
30731         
30732         this.grid.render();
30733         this.grid.getDataSource().on('remove', this.refreshValue, this);
30734         this.grid.getDataSource().on('update', this.refreshValue, this);
30735         this.grid.on('afteredit', this.refreshValue, this);
30736  
30737     },
30738      
30739     
30740     /**
30741      * Sets the value of the item. 
30742      * @param {String} either an object  or a string..
30743      */
30744     setValue : function(v){
30745         //this.value = v;
30746         v = v || []; // empty set..
30747         // this does not seem smart - it really only affects memoryproxy grids..
30748         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30749             var ds = this.grid.getDataSource();
30750             // assumes a json reader..
30751             var data = {}
30752             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30753             ds.loadData( data);
30754         }
30755         // clear selection so it does not get stale.
30756         if (this.grid.sm) { 
30757             this.grid.sm.clearSelections();
30758         }
30759         
30760         Roo.form.GridField.superclass.setValue.call(this, v);
30761         this.refreshValue();
30762         // should load data in the grid really....
30763     },
30764     
30765     // private
30766     refreshValue: function() {
30767          var val = [];
30768         this.grid.getDataSource().each(function(r) {
30769             val.push(r.data);
30770         });
30771         this.el.dom.value = Roo.encode(val);
30772     }
30773     
30774      
30775     
30776     
30777 });/*
30778  * Based on:
30779  * Ext JS Library 1.1.1
30780  * Copyright(c) 2006-2007, Ext JS, LLC.
30781  *
30782  * Originally Released Under LGPL - original licence link has changed is not relivant.
30783  *
30784  * Fork - LGPL
30785  * <script type="text/javascript">
30786  */
30787 /**
30788  * @class Roo.form.DisplayField
30789  * @extends Roo.form.Field
30790  * A generic Field to display non-editable data.
30791  * @constructor
30792  * Creates a new Display Field item.
30793  * @param {Object} config Configuration options
30794  */
30795 Roo.form.DisplayField = function(config){
30796     Roo.form.DisplayField.superclass.constructor.call(this, config);
30797     
30798 };
30799
30800 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30801     inputType:      'hidden',
30802     allowBlank:     true,
30803     readOnly:         true,
30804     
30805  
30806     /**
30807      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30808      */
30809     focusClass : undefined,
30810     /**
30811      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30812      */
30813     fieldClass: 'x-form-field',
30814     
30815      /**
30816      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30817      */
30818     valueRenderer: undefined,
30819     
30820     width: 100,
30821     /**
30822      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30823      * {tag: "input", type: "checkbox", autocomplete: "off"})
30824      */
30825      
30826  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30827
30828     onResize : function(){
30829         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30830         
30831     },
30832
30833     initEvents : function(){
30834         // Roo.form.Checkbox.superclass.initEvents.call(this);
30835         // has no events...
30836        
30837     },
30838
30839
30840     getResizeEl : function(){
30841         return this.wrap;
30842     },
30843
30844     getPositionEl : function(){
30845         return this.wrap;
30846     },
30847
30848     // private
30849     onRender : function(ct, position){
30850         
30851         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30852         //if(this.inputValue !== undefined){
30853         this.wrap = this.el.wrap();
30854         
30855         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30856         
30857         if (this.bodyStyle) {
30858             this.viewEl.applyStyles(this.bodyStyle);
30859         }
30860         //this.viewEl.setStyle('padding', '2px');
30861         
30862         this.setValue(this.value);
30863         
30864     },
30865 /*
30866     // private
30867     initValue : Roo.emptyFn,
30868
30869   */
30870
30871         // private
30872     onClick : function(){
30873         
30874     },
30875
30876     /**
30877      * Sets the checked state of the checkbox.
30878      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30879      */
30880     setValue : function(v){
30881         this.value = v;
30882         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30883         // this might be called before we have a dom element..
30884         if (!this.viewEl) {
30885             return;
30886         }
30887         this.viewEl.dom.innerHTML = html;
30888         Roo.form.DisplayField.superclass.setValue.call(this, v);
30889
30890     }
30891 });/*
30892  * 
30893  * Licence- LGPL
30894  * 
30895  */
30896
30897 /**
30898  * @class Roo.form.DayPicker
30899  * @extends Roo.form.Field
30900  * A Day picker show [M] [T] [W] ....
30901  * @constructor
30902  * Creates a new Day Picker
30903  * @param {Object} config Configuration options
30904  */
30905 Roo.form.DayPicker= function(config){
30906     Roo.form.DayPicker.superclass.constructor.call(this, config);
30907      
30908 };
30909
30910 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30911     /**
30912      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30913      */
30914     focusClass : undefined,
30915     /**
30916      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30917      */
30918     fieldClass: "x-form-field",
30919    
30920     /**
30921      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30922      * {tag: "input", type: "checkbox", autocomplete: "off"})
30923      */
30924     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30925     
30926    
30927     actionMode : 'viewEl', 
30928     //
30929     // private
30930  
30931     inputType : 'hidden',
30932     
30933      
30934     inputElement: false, // real input element?
30935     basedOn: false, // ????
30936     
30937     isFormField: true, // not sure where this is needed!!!!
30938
30939     onResize : function(){
30940         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30941         if(!this.boxLabel){
30942             this.el.alignTo(this.wrap, 'c-c');
30943         }
30944     },
30945
30946     initEvents : function(){
30947         Roo.form.Checkbox.superclass.initEvents.call(this);
30948         this.el.on("click", this.onClick,  this);
30949         this.el.on("change", this.onClick,  this);
30950     },
30951
30952
30953     getResizeEl : function(){
30954         return this.wrap;
30955     },
30956
30957     getPositionEl : function(){
30958         return this.wrap;
30959     },
30960
30961     
30962     // private
30963     onRender : function(ct, position){
30964         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30965        
30966         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30967         
30968         var r1 = '<table><tr>';
30969         var r2 = '<tr class="x-form-daypick-icons">';
30970         for (var i=0; i < 7; i++) {
30971             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30972             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30973         }
30974         
30975         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30976         viewEl.select('img').on('click', this.onClick, this);
30977         this.viewEl = viewEl;   
30978         
30979         
30980         // this will not work on Chrome!!!
30981         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30982         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30983         
30984         
30985           
30986
30987     },
30988
30989     // private
30990     initValue : Roo.emptyFn,
30991
30992     /**
30993      * Returns the checked state of the checkbox.
30994      * @return {Boolean} True if checked, else false
30995      */
30996     getValue : function(){
30997         return this.el.dom.value;
30998         
30999     },
31000
31001         // private
31002     onClick : function(e){ 
31003         //this.setChecked(!this.checked);
31004         Roo.get(e.target).toggleClass('x-menu-item-checked');
31005         this.refreshValue();
31006         //if(this.el.dom.checked != this.checked){
31007         //    this.setValue(this.el.dom.checked);
31008        // }
31009     },
31010     
31011     // private
31012     refreshValue : function()
31013     {
31014         var val = '';
31015         this.viewEl.select('img',true).each(function(e,i,n)  {
31016             val += e.is(".x-menu-item-checked") ? String(n) : '';
31017         });
31018         this.setValue(val, true);
31019     },
31020
31021     /**
31022      * Sets the checked state of the checkbox.
31023      * On is always based on a string comparison between inputValue and the param.
31024      * @param {Boolean/String} value - the value to set 
31025      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31026      */
31027     setValue : function(v,suppressEvent){
31028         if (!this.el.dom) {
31029             return;
31030         }
31031         var old = this.el.dom.value ;
31032         this.el.dom.value = v;
31033         if (suppressEvent) {
31034             return ;
31035         }
31036          
31037         // update display..
31038         this.viewEl.select('img',true).each(function(e,i,n)  {
31039             
31040             var on = e.is(".x-menu-item-checked");
31041             var newv = v.indexOf(String(n)) > -1;
31042             if (on != newv) {
31043                 e.toggleClass('x-menu-item-checked');
31044             }
31045             
31046         });
31047         
31048         
31049         this.fireEvent('change', this, v, old);
31050         
31051         
31052     },
31053    
31054     // handle setting of hidden value by some other method!!?!?
31055     setFromHidden: function()
31056     {
31057         if(!this.el){
31058             return;
31059         }
31060         //console.log("SET FROM HIDDEN");
31061         //alert('setFrom hidden');
31062         this.setValue(this.el.dom.value);
31063     },
31064     
31065     onDestroy : function()
31066     {
31067         if(this.viewEl){
31068             Roo.get(this.viewEl).remove();
31069         }
31070          
31071         Roo.form.DayPicker.superclass.onDestroy.call(this);
31072     }
31073
31074 });/*
31075  * RooJS Library 1.1.1
31076  * Copyright(c) 2008-2011  Alan Knowles
31077  *
31078  * License - LGPL
31079  */
31080  
31081
31082 /**
31083  * @class Roo.form.ComboCheck
31084  * @extends Roo.form.ComboBox
31085  * A combobox for multiple select items.
31086  *
31087  * FIXME - could do with a reset button..
31088  * 
31089  * @constructor
31090  * Create a new ComboCheck
31091  * @param {Object} config Configuration options
31092  */
31093 Roo.form.ComboCheck = function(config){
31094     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31095     // should verify some data...
31096     // like
31097     // hiddenName = required..
31098     // displayField = required
31099     // valudField == required
31100     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31101     var _t = this;
31102     Roo.each(req, function(e) {
31103         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31104             throw "Roo.form.ComboCheck : missing value for: " + e;
31105         }
31106     });
31107     
31108     
31109 };
31110
31111 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31112      
31113      
31114     editable : false,
31115      
31116     selectedClass: 'x-menu-item-checked', 
31117     
31118     // private
31119     onRender : function(ct, position){
31120         var _t = this;
31121         
31122         
31123         
31124         if(!this.tpl){
31125             var cls = 'x-combo-list';
31126
31127             
31128             this.tpl =  new Roo.Template({
31129                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31130                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31131                    '<span>{' + this.displayField + '}</span>' +
31132                     '</div>' 
31133                 
31134             });
31135         }
31136  
31137         
31138         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31139         this.view.singleSelect = false;
31140         this.view.multiSelect = true;
31141         this.view.toggleSelect = true;
31142         this.pageTb.add(new Roo.Toolbar.Fill(), {
31143             
31144             text: 'Done',
31145             handler: function()
31146             {
31147                 _t.collapse();
31148             }
31149         });
31150     },
31151     
31152     onViewOver : function(e, t){
31153         // do nothing...
31154         return;
31155         
31156     },
31157     
31158     onViewClick : function(doFocus,index){
31159         return;
31160         
31161     },
31162     select: function () {
31163         //Roo.log("SELECT CALLED");
31164     },
31165      
31166     selectByValue : function(xv, scrollIntoView){
31167         var ar = this.getValueArray();
31168         var sels = [];
31169         
31170         Roo.each(ar, function(v) {
31171             if(v === undefined || v === null){
31172                 return;
31173             }
31174             var r = this.findRecord(this.valueField, v);
31175             if(r){
31176                 sels.push(this.store.indexOf(r))
31177                 
31178             }
31179         },this);
31180         this.view.select(sels);
31181         return false;
31182     },
31183     
31184     
31185     
31186     onSelect : function(record, index){
31187        // Roo.log("onselect Called");
31188        // this is only called by the clear button now..
31189         this.view.clearSelections();
31190         this.setValue('[]');
31191         if (this.value != this.valueBefore) {
31192             this.fireEvent('change', this, this.value, this.valueBefore);
31193             this.valueBefore = this.value;
31194         }
31195     },
31196     getValueArray : function()
31197     {
31198         var ar = [] ;
31199         
31200         try {
31201             //Roo.log(this.value);
31202             if (typeof(this.value) == 'undefined') {
31203                 return [];
31204             }
31205             var ar = Roo.decode(this.value);
31206             return  ar instanceof Array ? ar : []; //?? valid?
31207             
31208         } catch(e) {
31209             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31210             return [];
31211         }
31212          
31213     },
31214     expand : function ()
31215     {
31216         
31217         Roo.form.ComboCheck.superclass.expand.call(this);
31218         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31219         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31220         
31221
31222     },
31223     
31224     collapse : function(){
31225         Roo.form.ComboCheck.superclass.collapse.call(this);
31226         var sl = this.view.getSelectedIndexes();
31227         var st = this.store;
31228         var nv = [];
31229         var tv = [];
31230         var r;
31231         Roo.each(sl, function(i) {
31232             r = st.getAt(i);
31233             nv.push(r.get(this.valueField));
31234         },this);
31235         this.setValue(Roo.encode(nv));
31236         if (this.value != this.valueBefore) {
31237
31238             this.fireEvent('change', this, this.value, this.valueBefore);
31239             this.valueBefore = this.value;
31240         }
31241         
31242     },
31243     
31244     setValue : function(v){
31245         // Roo.log(v);
31246         this.value = v;
31247         
31248         var vals = this.getValueArray();
31249         var tv = [];
31250         Roo.each(vals, function(k) {
31251             var r = this.findRecord(this.valueField, k);
31252             if(r){
31253                 tv.push(r.data[this.displayField]);
31254             }else if(this.valueNotFoundText !== undefined){
31255                 tv.push( this.valueNotFoundText );
31256             }
31257         },this);
31258        // Roo.log(tv);
31259         
31260         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31261         this.hiddenField.value = v;
31262         this.value = v;
31263     }
31264     
31265 });/*
31266  * Based on:
31267  * Ext JS Library 1.1.1
31268  * Copyright(c) 2006-2007, Ext JS, LLC.
31269  *
31270  * Originally Released Under LGPL - original licence link has changed is not relivant.
31271  *
31272  * Fork - LGPL
31273  * <script type="text/javascript">
31274  */
31275  
31276 /**
31277  * @class Roo.form.Signature
31278  * @extends Roo.form.Field
31279  * Signature field.  
31280  * @constructor
31281  * 
31282  * @param {Object} config Configuration options
31283  */
31284
31285 Roo.form.Signature = function(config){
31286     Roo.form.Signature.superclass.constructor.call(this, config);
31287     
31288     this.addEvents({// not in used??
31289          /**
31290          * @event confirm
31291          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31292              * @param {Roo.form.Signature} combo This combo box
31293              */
31294         'confirm' : true,
31295         /**
31296          * @event reset
31297          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31298              * @param {Roo.form.ComboBox} combo This combo box
31299              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31300              */
31301         'reset' : true
31302     });
31303 };
31304
31305 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31306     /**
31307      * @cfg {Object} labels Label to use when rendering a form.
31308      * defaults to 
31309      * labels : { 
31310      *      clear : "Clear",
31311      *      confirm : "Confirm"
31312      *  }
31313      */
31314     labels : { 
31315         clear : "Clear",
31316         confirm : "Confirm"
31317     },
31318     /**
31319      * @cfg {Number} width The signature panel width (defaults to 300)
31320      */
31321     width: 300,
31322     /**
31323      * @cfg {Number} height The signature panel height (defaults to 100)
31324      */
31325     height : 100,
31326     /**
31327      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31328      */
31329     allowBlank : false,
31330     
31331     //private
31332     // {Object} signPanel The signature SVG panel element (defaults to {})
31333     signPanel : {},
31334     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31335     isMouseDown : false,
31336     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31337     isConfirmed : false,
31338     // {String} signatureTmp SVG mapping string (defaults to empty string)
31339     signatureTmp : '',
31340     
31341     
31342     defaultAutoCreate : { // modified by initCompnoent..
31343         tag: "input",
31344         type:"hidden"
31345     },
31346
31347     // private
31348     onRender : function(ct, position){
31349         
31350         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31351         
31352         this.wrap = this.el.wrap({
31353             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31354         });
31355         
31356         this.createToolbar(this);
31357         this.signPanel = this.wrap.createChild({
31358                 tag: 'div',
31359                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31360             }, this.el
31361         );
31362             
31363         this.svgID = Roo.id();
31364         this.svgEl = this.signPanel.createChild({
31365               xmlns : 'http://www.w3.org/2000/svg',
31366               tag : 'svg',
31367               id : this.svgID + "-svg",
31368               width: this.width,
31369               height: this.height,
31370               viewBox: '0 0 '+this.width+' '+this.height,
31371               cn : [
31372                 {
31373                     tag: "rect",
31374                     id: this.svgID + "-svg-r",
31375                     width: this.width,
31376                     height: this.height,
31377                     fill: "#ffa"
31378                 },
31379                 {
31380                     tag: "line",
31381                     id: this.svgID + "-svg-l",
31382                     x1: "0", // start
31383                     y1: (this.height*0.8), // start set the line in 80% of height
31384                     x2: this.width, // end
31385                     y2: (this.height*0.8), // end set the line in 80% of height
31386                     'stroke': "#666",
31387                     'stroke-width': "1",
31388                     'stroke-dasharray': "3",
31389                     'shape-rendering': "crispEdges",
31390                     'pointer-events': "none"
31391                 },
31392                 {
31393                     tag: "path",
31394                     id: this.svgID + "-svg-p",
31395                     'stroke': "navy",
31396                     'stroke-width': "3",
31397                     'fill': "none",
31398                     'pointer-events': 'none'
31399                 }
31400               ]
31401         });
31402         this.createSVG();
31403         this.svgBox = this.svgEl.dom.getScreenCTM();
31404     },
31405     createSVG : function(){ 
31406         var svg = this.signPanel;
31407         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31408         var t = this;
31409
31410         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31411         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31412         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31413         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31414         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31415         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31416         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31417         
31418     },
31419     isTouchEvent : function(e){
31420         return e.type.match(/^touch/);
31421     },
31422     getCoords : function (e) {
31423         var pt    = this.svgEl.dom.createSVGPoint();
31424         pt.x = e.clientX; 
31425         pt.y = e.clientY;
31426         if (this.isTouchEvent(e)) {
31427             pt.x =  e.targetTouches[0].clientX 
31428             pt.y = e.targetTouches[0].clientY;
31429         }
31430         var a = this.svgEl.dom.getScreenCTM();
31431         var b = a.inverse();
31432         var mx = pt.matrixTransform(b);
31433         return mx.x + ',' + mx.y;
31434     },
31435     //mouse event headler 
31436     down : function (e) {
31437         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31438         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31439         
31440         this.isMouseDown = true;
31441         
31442         e.preventDefault();
31443     },
31444     move : function (e) {
31445         if (this.isMouseDown) {
31446             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31447             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31448         }
31449         
31450         e.preventDefault();
31451     },
31452     up : function (e) {
31453         this.isMouseDown = false;
31454         var sp = this.signatureTmp.split(' ');
31455         
31456         if(sp.length > 1){
31457             if(!sp[sp.length-2].match(/^L/)){
31458                 sp.pop();
31459                 sp.pop();
31460                 sp.push("");
31461                 this.signatureTmp = sp.join(" ");
31462             }
31463         }
31464         if(this.getValue() != this.signatureTmp){
31465             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31466             this.isConfirmed = false;
31467         }
31468         e.preventDefault();
31469     },
31470     
31471     /**
31472      * Protected method that will not generally be called directly. It
31473      * is called when the editor creates its toolbar. Override this method if you need to
31474      * add custom toolbar buttons.
31475      * @param {HtmlEditor} editor
31476      */
31477     createToolbar : function(editor){
31478          function btn(id, toggle, handler){
31479             var xid = fid + '-'+ id ;
31480             return {
31481                 id : xid,
31482                 cmd : id,
31483                 cls : 'x-btn-icon x-edit-'+id,
31484                 enableToggle:toggle !== false,
31485                 scope: editor, // was editor...
31486                 handler:handler||editor.relayBtnCmd,
31487                 clickEvent:'mousedown',
31488                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31489                 tabIndex:-1
31490             };
31491         }
31492         
31493         
31494         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31495         this.tb = tb;
31496         this.tb.add(
31497            {
31498                 cls : ' x-signature-btn x-signature-'+id,
31499                 scope: editor, // was editor...
31500                 handler: this.reset,
31501                 clickEvent:'mousedown',
31502                 text: this.labels.clear
31503             },
31504             {
31505                  xtype : 'Fill',
31506                  xns: Roo.Toolbar
31507             }, 
31508             {
31509                 cls : '  x-signature-btn x-signature-'+id,
31510                 scope: editor, // was editor...
31511                 handler: this.confirmHandler,
31512                 clickEvent:'mousedown',
31513                 text: this.labels.confirm
31514             }
31515         );
31516     
31517     },
31518     //public
31519     /**
31520      * when user is clicked confirm then show this image.....
31521      * 
31522      * @return {String} Image Data URI
31523      */
31524     getImageDataURI : function(){
31525         var svg = this.svgEl.dom.parentNode.innerHTML;
31526         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31527         return src; 
31528     },
31529     /**
31530      * 
31531      * @return {Boolean} this.isConfirmed
31532      */
31533     getConfirmed : function(){
31534         return this.isConfirmed;
31535     },
31536     /**
31537      * 
31538      * @return {Number} this.width
31539      */
31540     getWidth : function(){
31541         return this.width;
31542     },
31543     /**
31544      * 
31545      * @return {Number} this.height
31546      */
31547     getHeight : function(){
31548         return this.height;
31549     },
31550     // private
31551     getSignature : function(){
31552         return this.signatureTmp;
31553     },
31554     // private
31555     reset : function(){
31556         this.signatureTmp = '';
31557         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31558         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31559         this.isConfirmed = false;
31560         Roo.form.Signature.superclass.reset.call(this);
31561     },
31562     setSignature : function(s){
31563         this.signatureTmp = s;
31564         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31565         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31566         this.setValue(s);
31567         this.isConfirmed = false;
31568         Roo.form.Signature.superclass.reset.call(this);
31569     }, 
31570     test : function(){
31571 //        Roo.log(this.signPanel.dom.contentWindow.up())
31572     },
31573     //private
31574     setConfirmed : function(){
31575         
31576         
31577         
31578 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31579     },
31580     // private
31581     confirmHandler : function(){
31582         if(!this.getSignature()){
31583             return;
31584         }
31585         
31586         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31587         this.setValue(this.getSignature());
31588         this.isConfirmed = true;
31589         
31590         this.fireEvent('confirm', this);
31591     },
31592     // private
31593     // Subclasses should provide the validation implementation by overriding this
31594     validateValue : function(value){
31595         if(this.allowBlank){
31596             return true;
31597         }
31598         
31599         if(this.isConfirmed){
31600             return true;
31601         }
31602         return false;
31603     }
31604 });/*
31605  * Based on:
31606  * Ext JS Library 1.1.1
31607  * Copyright(c) 2006-2007, Ext JS, LLC.
31608  *
31609  * Originally Released Under LGPL - original licence link has changed is not relivant.
31610  *
31611  * Fork - LGPL
31612  * <script type="text/javascript">
31613  */
31614  
31615
31616 /**
31617  * @class Roo.form.ComboBox
31618  * @extends Roo.form.TriggerField
31619  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31620  * @constructor
31621  * Create a new ComboBox.
31622  * @param {Object} config Configuration options
31623  */
31624 Roo.form.Select = function(config){
31625     Roo.form.Select.superclass.constructor.call(this, config);
31626      
31627 };
31628
31629 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31630     /**
31631      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31632      */
31633     /**
31634      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31635      * rendering into an Roo.Editor, defaults to false)
31636      */
31637     /**
31638      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31639      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31640      */
31641     /**
31642      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31643      */
31644     /**
31645      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31646      * the dropdown list (defaults to undefined, with no header element)
31647      */
31648
31649      /**
31650      * @cfg {String/Roo.Template} tpl The template to use to render the output
31651      */
31652      
31653     // private
31654     defaultAutoCreate : {tag: "select"  },
31655     /**
31656      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31657      */
31658     listWidth: undefined,
31659     /**
31660      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31661      * mode = 'remote' or 'text' if mode = 'local')
31662      */
31663     displayField: undefined,
31664     /**
31665      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31666      * mode = 'remote' or 'value' if mode = 'local'). 
31667      * Note: use of a valueField requires the user make a selection
31668      * in order for a value to be mapped.
31669      */
31670     valueField: undefined,
31671     
31672     
31673     /**
31674      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31675      * field's data value (defaults to the underlying DOM element's name)
31676      */
31677     hiddenName: undefined,
31678     /**
31679      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31680      */
31681     listClass: '',
31682     /**
31683      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31684      */
31685     selectedClass: 'x-combo-selected',
31686     /**
31687      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31688      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31689      * which displays a downward arrow icon).
31690      */
31691     triggerClass : 'x-form-arrow-trigger',
31692     /**
31693      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31694      */
31695     shadow:'sides',
31696     /**
31697      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31698      * anchor positions (defaults to 'tl-bl')
31699      */
31700     listAlign: 'tl-bl?',
31701     /**
31702      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31703      */
31704     maxHeight: 300,
31705     /**
31706      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31707      * query specified by the allQuery config option (defaults to 'query')
31708      */
31709     triggerAction: 'query',
31710     /**
31711      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31712      * (defaults to 4, does not apply if editable = false)
31713      */
31714     minChars : 4,
31715     /**
31716      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31717      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31718      */
31719     typeAhead: false,
31720     /**
31721      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31722      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31723      */
31724     queryDelay: 500,
31725     /**
31726      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31727      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31728      */
31729     pageSize: 0,
31730     /**
31731      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31732      * when editable = true (defaults to false)
31733      */
31734     selectOnFocus:false,
31735     /**
31736      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31737      */
31738     queryParam: 'query',
31739     /**
31740      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31741      * when mode = 'remote' (defaults to 'Loading...')
31742      */
31743     loadingText: 'Loading...',
31744     /**
31745      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31746      */
31747     resizable: false,
31748     /**
31749      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31750      */
31751     handleHeight : 8,
31752     /**
31753      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31754      * traditional select (defaults to true)
31755      */
31756     editable: true,
31757     /**
31758      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31759      */
31760     allQuery: '',
31761     /**
31762      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31763      */
31764     mode: 'remote',
31765     /**
31766      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31767      * listWidth has a higher value)
31768      */
31769     minListWidth : 70,
31770     /**
31771      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31772      * allow the user to set arbitrary text into the field (defaults to false)
31773      */
31774     forceSelection:false,
31775     /**
31776      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31777      * if typeAhead = true (defaults to 250)
31778      */
31779     typeAheadDelay : 250,
31780     /**
31781      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31782      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31783      */
31784     valueNotFoundText : undefined,
31785     
31786     /**
31787      * @cfg {String} defaultValue The value displayed after loading the store.
31788      */
31789     defaultValue: '',
31790     
31791     /**
31792      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31793      */
31794     blockFocus : false,
31795     
31796     /**
31797      * @cfg {Boolean} disableClear Disable showing of clear button.
31798      */
31799     disableClear : false,
31800     /**
31801      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31802      */
31803     alwaysQuery : false,
31804     
31805     //private
31806     addicon : false,
31807     editicon: false,
31808     
31809     // element that contains real text value.. (when hidden is used..)
31810      
31811     // private
31812     onRender : function(ct, position){
31813         Roo.form.Field.prototype.onRender.call(this, ct, position);
31814         
31815         if(this.store){
31816             this.store.on('beforeload', this.onBeforeLoad, this);
31817             this.store.on('load', this.onLoad, this);
31818             this.store.on('loadexception', this.onLoadException, this);
31819             this.store.load({});
31820         }
31821         
31822         
31823         
31824     },
31825
31826     // private
31827     initEvents : function(){
31828         //Roo.form.ComboBox.superclass.initEvents.call(this);
31829  
31830     },
31831
31832     onDestroy : function(){
31833        
31834         if(this.store){
31835             this.store.un('beforeload', this.onBeforeLoad, this);
31836             this.store.un('load', this.onLoad, this);
31837             this.store.un('loadexception', this.onLoadException, this);
31838         }
31839         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31840     },
31841
31842     // private
31843     fireKey : function(e){
31844         if(e.isNavKeyPress() && !this.list.isVisible()){
31845             this.fireEvent("specialkey", this, e);
31846         }
31847     },
31848
31849     // private
31850     onResize: function(w, h){
31851         
31852         return; 
31853     
31854         
31855     },
31856
31857     /**
31858      * Allow or prevent the user from directly editing the field text.  If false is passed,
31859      * the user will only be able to select from the items defined in the dropdown list.  This method
31860      * is the runtime equivalent of setting the 'editable' config option at config time.
31861      * @param {Boolean} value True to allow the user to directly edit the field text
31862      */
31863     setEditable : function(value){
31864          
31865     },
31866
31867     // private
31868     onBeforeLoad : function(){
31869         
31870         Roo.log("Select before load");
31871         return;
31872     
31873         this.innerList.update(this.loadingText ?
31874                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31875         //this.restrictHeight();
31876         this.selectedIndex = -1;
31877     },
31878
31879     // private
31880     onLoad : function(){
31881
31882     
31883         var dom = this.el.dom;
31884         dom.innerHTML = '';
31885          var od = dom.ownerDocument;
31886          
31887         if (this.emptyText) {
31888             var op = od.createElement('option');
31889             op.setAttribute('value', '');
31890             op.innerHTML = String.format('{0}', this.emptyText);
31891             dom.appendChild(op);
31892         }
31893         if(this.store.getCount() > 0){
31894            
31895             var vf = this.valueField;
31896             var df = this.displayField;
31897             this.store.data.each(function(r) {
31898                 // which colmsn to use... testing - cdoe / title..
31899                 var op = od.createElement('option');
31900                 op.setAttribute('value', r.data[vf]);
31901                 op.innerHTML = String.format('{0}', r.data[df]);
31902                 dom.appendChild(op);
31903             });
31904             if (typeof(this.defaultValue != 'undefined')) {
31905                 this.setValue(this.defaultValue);
31906             }
31907             
31908              
31909         }else{
31910             //this.onEmptyResults();
31911         }
31912         //this.el.focus();
31913     },
31914     // private
31915     onLoadException : function()
31916     {
31917         dom.innerHTML = '';
31918             
31919         Roo.log("Select on load exception");
31920         return;
31921     
31922         this.collapse();
31923         Roo.log(this.store.reader.jsonData);
31924         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31925             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31926         }
31927         
31928         
31929     },
31930     // private
31931     onTypeAhead : function(){
31932          
31933     },
31934
31935     // private
31936     onSelect : function(record, index){
31937         Roo.log('on select?');
31938         return;
31939         if(this.fireEvent('beforeselect', this, record, index) !== false){
31940             this.setFromData(index > -1 ? record.data : false);
31941             this.collapse();
31942             this.fireEvent('select', this, record, index);
31943         }
31944     },
31945
31946     /**
31947      * Returns the currently selected field value or empty string if no value is set.
31948      * @return {String} value The selected value
31949      */
31950     getValue : function(){
31951         var dom = this.el.dom;
31952         this.value = dom.options[dom.selectedIndex].value;
31953         return this.value;
31954         
31955     },
31956
31957     /**
31958      * Clears any text/value currently set in the field
31959      */
31960     clearValue : function(){
31961         this.value = '';
31962         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31963         
31964     },
31965
31966     /**
31967      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31968      * will be displayed in the field.  If the value does not match the data value of an existing item,
31969      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31970      * Otherwise the field will be blank (although the value will still be set).
31971      * @param {String} value The value to match
31972      */
31973     setValue : function(v){
31974         var d = this.el.dom;
31975         for (var i =0; i < d.options.length;i++) {
31976             if (v == d.options[i].value) {
31977                 d.selectedIndex = i;
31978                 this.value = v;
31979                 return;
31980             }
31981         }
31982         this.clearValue();
31983     },
31984     /**
31985      * @property {Object} the last set data for the element
31986      */
31987     
31988     lastData : false,
31989     /**
31990      * Sets the value of the field based on a object which is related to the record format for the store.
31991      * @param {Object} value the value to set as. or false on reset?
31992      */
31993     setFromData : function(o){
31994         Roo.log('setfrom data?');
31995          
31996         
31997         
31998     },
31999     // private
32000     reset : function(){
32001         this.clearValue();
32002     },
32003     // private
32004     findRecord : function(prop, value){
32005         
32006         return false;
32007     
32008         var record;
32009         if(this.store.getCount() > 0){
32010             this.store.each(function(r){
32011                 if(r.data[prop] == value){
32012                     record = r;
32013                     return false;
32014                 }
32015                 return true;
32016             });
32017         }
32018         return record;
32019     },
32020     
32021     getName: function()
32022     {
32023         // returns hidden if it's set..
32024         if (!this.rendered) {return ''};
32025         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32026         
32027     },
32028      
32029
32030     
32031
32032     // private
32033     onEmptyResults : function(){
32034         Roo.log('empty results');
32035         //this.collapse();
32036     },
32037
32038     /**
32039      * Returns true if the dropdown list is expanded, else false.
32040      */
32041     isExpanded : function(){
32042         return false;
32043     },
32044
32045     /**
32046      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32047      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32048      * @param {String} value The data value of the item to select
32049      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32050      * selected item if it is not currently in view (defaults to true)
32051      * @return {Boolean} True if the value matched an item in the list, else false
32052      */
32053     selectByValue : function(v, scrollIntoView){
32054         Roo.log('select By Value');
32055         return false;
32056     
32057         if(v !== undefined && v !== null){
32058             var r = this.findRecord(this.valueField || this.displayField, v);
32059             if(r){
32060                 this.select(this.store.indexOf(r), scrollIntoView);
32061                 return true;
32062             }
32063         }
32064         return false;
32065     },
32066
32067     /**
32068      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32069      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32070      * @param {Number} index The zero-based index of the list item to select
32071      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32072      * selected item if it is not currently in view (defaults to true)
32073      */
32074     select : function(index, scrollIntoView){
32075         Roo.log('select ');
32076         return  ;
32077         
32078         this.selectedIndex = index;
32079         this.view.select(index);
32080         if(scrollIntoView !== false){
32081             var el = this.view.getNode(index);
32082             if(el){
32083                 this.innerList.scrollChildIntoView(el, false);
32084             }
32085         }
32086     },
32087
32088       
32089
32090     // private
32091     validateBlur : function(){
32092         
32093         return;
32094         
32095     },
32096
32097     // private
32098     initQuery : function(){
32099         this.doQuery(this.getRawValue());
32100     },
32101
32102     // private
32103     doForce : function(){
32104         if(this.el.dom.value.length > 0){
32105             this.el.dom.value =
32106                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32107              
32108         }
32109     },
32110
32111     /**
32112      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32113      * query allowing the query action to be canceled if needed.
32114      * @param {String} query The SQL query to execute
32115      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32116      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32117      * saved in the current store (defaults to false)
32118      */
32119     doQuery : function(q, forceAll){
32120         
32121         Roo.log('doQuery?');
32122         if(q === undefined || q === null){
32123             q = '';
32124         }
32125         var qe = {
32126             query: q,
32127             forceAll: forceAll,
32128             combo: this,
32129             cancel:false
32130         };
32131         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32132             return false;
32133         }
32134         q = qe.query;
32135         forceAll = qe.forceAll;
32136         if(forceAll === true || (q.length >= this.minChars)){
32137             if(this.lastQuery != q || this.alwaysQuery){
32138                 this.lastQuery = q;
32139                 if(this.mode == 'local'){
32140                     this.selectedIndex = -1;
32141                     if(forceAll){
32142                         this.store.clearFilter();
32143                     }else{
32144                         this.store.filter(this.displayField, q);
32145                     }
32146                     this.onLoad();
32147                 }else{
32148                     this.store.baseParams[this.queryParam] = q;
32149                     this.store.load({
32150                         params: this.getParams(q)
32151                     });
32152                     this.expand();
32153                 }
32154             }else{
32155                 this.selectedIndex = -1;
32156                 this.onLoad();   
32157             }
32158         }
32159     },
32160
32161     // private
32162     getParams : function(q){
32163         var p = {};
32164         //p[this.queryParam] = q;
32165         if(this.pageSize){
32166             p.start = 0;
32167             p.limit = this.pageSize;
32168         }
32169         return p;
32170     },
32171
32172     /**
32173      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32174      */
32175     collapse : function(){
32176         
32177     },
32178
32179     // private
32180     collapseIf : function(e){
32181         
32182     },
32183
32184     /**
32185      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32186      */
32187     expand : function(){
32188         
32189     } ,
32190
32191     // private
32192      
32193
32194     /** 
32195     * @cfg {Boolean} grow 
32196     * @hide 
32197     */
32198     /** 
32199     * @cfg {Number} growMin 
32200     * @hide 
32201     */
32202     /** 
32203     * @cfg {Number} growMax 
32204     * @hide 
32205     */
32206     /**
32207      * @hide
32208      * @method autoSize
32209      */
32210     
32211     setWidth : function()
32212     {
32213         
32214     },
32215     getResizeEl : function(){
32216         return this.el;
32217     }
32218 });//<script type="text/javasscript">
32219  
32220
32221 /**
32222  * @class Roo.DDView
32223  * A DnD enabled version of Roo.View.
32224  * @param {Element/String} container The Element in which to create the View.
32225  * @param {String} tpl The template string used to create the markup for each element of the View
32226  * @param {Object} config The configuration properties. These include all the config options of
32227  * {@link Roo.View} plus some specific to this class.<br>
32228  * <p>
32229  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32230  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32231  * <p>
32232  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32233 .x-view-drag-insert-above {
32234         border-top:1px dotted #3366cc;
32235 }
32236 .x-view-drag-insert-below {
32237         border-bottom:1px dotted #3366cc;
32238 }
32239 </code></pre>
32240  * 
32241  */
32242  
32243 Roo.DDView = function(container, tpl, config) {
32244     Roo.DDView.superclass.constructor.apply(this, arguments);
32245     this.getEl().setStyle("outline", "0px none");
32246     this.getEl().unselectable();
32247     if (this.dragGroup) {
32248                 this.setDraggable(this.dragGroup.split(","));
32249     }
32250     if (this.dropGroup) {
32251                 this.setDroppable(this.dropGroup.split(","));
32252     }
32253     if (this.deletable) {
32254         this.setDeletable();
32255     }
32256     this.isDirtyFlag = false;
32257         this.addEvents({
32258                 "drop" : true
32259         });
32260 };
32261
32262 Roo.extend(Roo.DDView, Roo.View, {
32263 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32264 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32265 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32266 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32267
32268         isFormField: true,
32269
32270         reset: Roo.emptyFn,
32271         
32272         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32273
32274         validate: function() {
32275                 return true;
32276         },
32277         
32278         destroy: function() {
32279                 this.purgeListeners();
32280                 this.getEl.removeAllListeners();
32281                 this.getEl().remove();
32282                 if (this.dragZone) {
32283                         if (this.dragZone.destroy) {
32284                                 this.dragZone.destroy();
32285                         }
32286                 }
32287                 if (this.dropZone) {
32288                         if (this.dropZone.destroy) {
32289                                 this.dropZone.destroy();
32290                         }
32291                 }
32292         },
32293
32294 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32295         getName: function() {
32296                 return this.name;
32297         },
32298
32299 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32300         setValue: function(v) {
32301                 if (!this.store) {
32302                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32303                 }
32304                 var data = {};
32305                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32306                 this.store.proxy = new Roo.data.MemoryProxy(data);
32307                 this.store.load();
32308         },
32309
32310 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32311         getValue: function() {
32312                 var result = '(';
32313                 this.store.each(function(rec) {
32314                         result += rec.id + ',';
32315                 });
32316                 return result.substr(0, result.length - 1) + ')';
32317         },
32318         
32319         getIds: function() {
32320                 var i = 0, result = new Array(this.store.getCount());
32321                 this.store.each(function(rec) {
32322                         result[i++] = rec.id;
32323                 });
32324                 return result;
32325         },
32326         
32327         isDirty: function() {
32328                 return this.isDirtyFlag;
32329         },
32330
32331 /**
32332  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32333  *      whole Element becomes the target, and this causes the drop gesture to append.
32334  */
32335     getTargetFromEvent : function(e) {
32336                 var target = e.getTarget();
32337                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32338                 target = target.parentNode;
32339                 }
32340                 if (!target) {
32341                         target = this.el.dom.lastChild || this.el.dom;
32342                 }
32343                 return target;
32344     },
32345
32346 /**
32347  *      Create the drag data which consists of an object which has the property "ddel" as
32348  *      the drag proxy element. 
32349  */
32350     getDragData : function(e) {
32351         var target = this.findItemFromChild(e.getTarget());
32352                 if(target) {
32353                         this.handleSelection(e);
32354                         var selNodes = this.getSelectedNodes();
32355             var dragData = {
32356                 source: this,
32357                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32358                 nodes: selNodes,
32359                 records: []
32360                         };
32361                         var selectedIndices = this.getSelectedIndexes();
32362                         for (var i = 0; i < selectedIndices.length; i++) {
32363                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32364                         }
32365                         if (selNodes.length == 1) {
32366                                 dragData.ddel = target.cloneNode(true); // the div element
32367                         } else {
32368                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32369                                 div.className = 'multi-proxy';
32370                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32371                                         div.appendChild(selNodes[i].cloneNode(true));
32372                                 }
32373                                 dragData.ddel = div;
32374                         }
32375             //console.log(dragData)
32376             //console.log(dragData.ddel.innerHTML)
32377                         return dragData;
32378                 }
32379         //console.log('nodragData')
32380                 return false;
32381     },
32382     
32383 /**     Specify to which ddGroup items in this DDView may be dragged. */
32384     setDraggable: function(ddGroup) {
32385         if (ddGroup instanceof Array) {
32386                 Roo.each(ddGroup, this.setDraggable, this);
32387                 return;
32388         }
32389         if (this.dragZone) {
32390                 this.dragZone.addToGroup(ddGroup);
32391         } else {
32392                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32393                                 containerScroll: true,
32394                                 ddGroup: ddGroup 
32395
32396                         });
32397 //                      Draggability implies selection. DragZone's mousedown selects the element.
32398                         if (!this.multiSelect) { this.singleSelect = true; }
32399
32400 //                      Wire the DragZone's handlers up to methods in *this*
32401                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32402                 }
32403     },
32404
32405 /**     Specify from which ddGroup this DDView accepts drops. */
32406     setDroppable: function(ddGroup) {
32407         if (ddGroup instanceof Array) {
32408                 Roo.each(ddGroup, this.setDroppable, this);
32409                 return;
32410         }
32411         if (this.dropZone) {
32412                 this.dropZone.addToGroup(ddGroup);
32413         } else {
32414                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32415                                 containerScroll: true,
32416                                 ddGroup: ddGroup
32417                         });
32418
32419 //                      Wire the DropZone's handlers up to methods in *this*
32420                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32421                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32422                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32423                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32424                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32425                 }
32426     },
32427
32428 /**     Decide whether to drop above or below a View node. */
32429     getDropPoint : function(e, n, dd){
32430         if (n == this.el.dom) { return "above"; }
32431                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32432                 var c = t + (b - t) / 2;
32433                 var y = Roo.lib.Event.getPageY(e);
32434                 if(y <= c) {
32435                         return "above";
32436                 }else{
32437                         return "below";
32438                 }
32439     },
32440
32441     onNodeEnter : function(n, dd, e, data){
32442                 return false;
32443     },
32444     
32445     onNodeOver : function(n, dd, e, data){
32446                 var pt = this.getDropPoint(e, n, dd);
32447                 // set the insert point style on the target node
32448                 var dragElClass = this.dropNotAllowed;
32449                 if (pt) {
32450                         var targetElClass;
32451                         if (pt == "above"){
32452                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32453                                 targetElClass = "x-view-drag-insert-above";
32454                         } else {
32455                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32456                                 targetElClass = "x-view-drag-insert-below";
32457                         }
32458                         if (this.lastInsertClass != targetElClass){
32459                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32460                                 this.lastInsertClass = targetElClass;
32461                         }
32462                 }
32463                 return dragElClass;
32464         },
32465
32466     onNodeOut : function(n, dd, e, data){
32467                 this.removeDropIndicators(n);
32468     },
32469
32470     onNodeDrop : function(n, dd, e, data){
32471         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32472                 return false;
32473         }
32474         var pt = this.getDropPoint(e, n, dd);
32475                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32476                 if (pt == "below") { insertAt++; }
32477                 for (var i = 0; i < data.records.length; i++) {
32478                         var r = data.records[i];
32479                         var dup = this.store.getById(r.id);
32480                         if (dup && (dd != this.dragZone)) {
32481                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32482                         } else {
32483                                 if (data.copy) {
32484                                         this.store.insert(insertAt++, r.copy());
32485                                 } else {
32486                                         data.source.isDirtyFlag = true;
32487                                         r.store.remove(r);
32488                                         this.store.insert(insertAt++, r);
32489                                 }
32490                                 this.isDirtyFlag = true;
32491                         }
32492                 }
32493                 this.dragZone.cachedTarget = null;
32494                 return true;
32495     },
32496
32497     removeDropIndicators : function(n){
32498                 if(n){
32499                         Roo.fly(n).removeClass([
32500                                 "x-view-drag-insert-above",
32501                                 "x-view-drag-insert-below"]);
32502                         this.lastInsertClass = "_noclass";
32503                 }
32504     },
32505
32506 /**
32507  *      Utility method. Add a delete option to the DDView's context menu.
32508  *      @param {String} imageUrl The URL of the "delete" icon image.
32509  */
32510         setDeletable: function(imageUrl) {
32511                 if (!this.singleSelect && !this.multiSelect) {
32512                         this.singleSelect = true;
32513                 }
32514                 var c = this.getContextMenu();
32515                 this.contextMenu.on("itemclick", function(item) {
32516                         switch (item.id) {
32517                                 case "delete":
32518                                         this.remove(this.getSelectedIndexes());
32519                                         break;
32520                         }
32521                 }, this);
32522                 this.contextMenu.add({
32523                         icon: imageUrl,
32524                         id: "delete",
32525                         text: 'Delete'
32526                 });
32527         },
32528         
32529 /**     Return the context menu for this DDView. */
32530         getContextMenu: function() {
32531                 if (!this.contextMenu) {
32532 //                      Create the View's context menu
32533                         this.contextMenu = new Roo.menu.Menu({
32534                                 id: this.id + "-contextmenu"
32535                         });
32536                         this.el.on("contextmenu", this.showContextMenu, this);
32537                 }
32538                 return this.contextMenu;
32539         },
32540         
32541         disableContextMenu: function() {
32542                 if (this.contextMenu) {
32543                         this.el.un("contextmenu", this.showContextMenu, this);
32544                 }
32545         },
32546
32547         showContextMenu: function(e, item) {
32548         item = this.findItemFromChild(e.getTarget());
32549                 if (item) {
32550                         e.stopEvent();
32551                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32552                         this.contextMenu.showAt(e.getXY());
32553             }
32554     },
32555
32556 /**
32557  *      Remove {@link Roo.data.Record}s at the specified indices.
32558  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32559  */
32560     remove: function(selectedIndices) {
32561                 selectedIndices = [].concat(selectedIndices);
32562                 for (var i = 0; i < selectedIndices.length; i++) {
32563                         var rec = this.store.getAt(selectedIndices[i]);
32564                         this.store.remove(rec);
32565                 }
32566     },
32567
32568 /**
32569  *      Double click fires the event, but also, if this is draggable, and there is only one other
32570  *      related DropZone, it transfers the selected node.
32571  */
32572     onDblClick : function(e){
32573         var item = this.findItemFromChild(e.getTarget());
32574         if(item){
32575             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32576                 return false;
32577             }
32578             if (this.dragGroup) {
32579                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32580                     while (targets.indexOf(this.dropZone) > -1) {
32581                             targets.remove(this.dropZone);
32582                                 }
32583                     if (targets.length == 1) {
32584                                         this.dragZone.cachedTarget = null;
32585                         var el = Roo.get(targets[0].getEl());
32586                         var box = el.getBox(true);
32587                         targets[0].onNodeDrop(el.dom, {
32588                                 target: el.dom,
32589                                 xy: [box.x, box.y + box.height - 1]
32590                         }, null, this.getDragData(e));
32591                     }
32592                 }
32593         }
32594     },
32595     
32596     handleSelection: function(e) {
32597                 this.dragZone.cachedTarget = null;
32598         var item = this.findItemFromChild(e.getTarget());
32599         if (!item) {
32600                 this.clearSelections(true);
32601                 return;
32602         }
32603                 if (item && (this.multiSelect || this.singleSelect)){
32604                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32605                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32606                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32607                                 this.unselect(item);
32608                         } else {
32609                                 this.select(item, this.multiSelect && e.ctrlKey);
32610                                 this.lastSelection = item;
32611                         }
32612                 }
32613     },
32614
32615     onItemClick : function(item, index, e){
32616                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32617                         return false;
32618                 }
32619                 return true;
32620     },
32621
32622     unselect : function(nodeInfo, suppressEvent){
32623                 var node = this.getNode(nodeInfo);
32624                 if(node && this.isSelected(node)){
32625                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32626                                 Roo.fly(node).removeClass(this.selectedClass);
32627                                 this.selections.remove(node);
32628                                 if(!suppressEvent){
32629                                         this.fireEvent("selectionchange", this, this.selections);
32630                                 }
32631                         }
32632                 }
32633     }
32634 });
32635 /*
32636  * Based on:
32637  * Ext JS Library 1.1.1
32638  * Copyright(c) 2006-2007, Ext JS, LLC.
32639  *
32640  * Originally Released Under LGPL - original licence link has changed is not relivant.
32641  *
32642  * Fork - LGPL
32643  * <script type="text/javascript">
32644  */
32645  
32646 /**
32647  * @class Roo.LayoutManager
32648  * @extends Roo.util.Observable
32649  * Base class for layout managers.
32650  */
32651 Roo.LayoutManager = function(container, config){
32652     Roo.LayoutManager.superclass.constructor.call(this);
32653     this.el = Roo.get(container);
32654     // ie scrollbar fix
32655     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32656         document.body.scroll = "no";
32657     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32658         this.el.position('relative');
32659     }
32660     this.id = this.el.id;
32661     this.el.addClass("x-layout-container");
32662     /** false to disable window resize monitoring @type Boolean */
32663     this.monitorWindowResize = true;
32664     this.regions = {};
32665     this.addEvents({
32666         /**
32667          * @event layout
32668          * Fires when a layout is performed. 
32669          * @param {Roo.LayoutManager} this
32670          */
32671         "layout" : true,
32672         /**
32673          * @event regionresized
32674          * Fires when the user resizes a region. 
32675          * @param {Roo.LayoutRegion} region The resized region
32676          * @param {Number} newSize The new size (width for east/west, height for north/south)
32677          */
32678         "regionresized" : true,
32679         /**
32680          * @event regioncollapsed
32681          * Fires when a region is collapsed. 
32682          * @param {Roo.LayoutRegion} region The collapsed region
32683          */
32684         "regioncollapsed" : true,
32685         /**
32686          * @event regionexpanded
32687          * Fires when a region is expanded.  
32688          * @param {Roo.LayoutRegion} region The expanded region
32689          */
32690         "regionexpanded" : true
32691     });
32692     this.updating = false;
32693     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32694 };
32695
32696 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32697     /**
32698      * Returns true if this layout is currently being updated
32699      * @return {Boolean}
32700      */
32701     isUpdating : function(){
32702         return this.updating; 
32703     },
32704     
32705     /**
32706      * Suspend the LayoutManager from doing auto-layouts while
32707      * making multiple add or remove calls
32708      */
32709     beginUpdate : function(){
32710         this.updating = true;    
32711     },
32712     
32713     /**
32714      * Restore auto-layouts and optionally disable the manager from performing a layout
32715      * @param {Boolean} noLayout true to disable a layout update 
32716      */
32717     endUpdate : function(noLayout){
32718         this.updating = false;
32719         if(!noLayout){
32720             this.layout();
32721         }    
32722     },
32723     
32724     layout: function(){
32725         
32726     },
32727     
32728     onRegionResized : function(region, newSize){
32729         this.fireEvent("regionresized", region, newSize);
32730         this.layout();
32731     },
32732     
32733     onRegionCollapsed : function(region){
32734         this.fireEvent("regioncollapsed", region);
32735     },
32736     
32737     onRegionExpanded : function(region){
32738         this.fireEvent("regionexpanded", region);
32739     },
32740         
32741     /**
32742      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32743      * performs box-model adjustments.
32744      * @return {Object} The size as an object {width: (the width), height: (the height)}
32745      */
32746     getViewSize : function(){
32747         var size;
32748         if(this.el.dom != document.body){
32749             size = this.el.getSize();
32750         }else{
32751             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32752         }
32753         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32754         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32755         return size;
32756     },
32757     
32758     /**
32759      * Returns the Element this layout is bound to.
32760      * @return {Roo.Element}
32761      */
32762     getEl : function(){
32763         return this.el;
32764     },
32765     
32766     /**
32767      * Returns the specified region.
32768      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32769      * @return {Roo.LayoutRegion}
32770      */
32771     getRegion : function(target){
32772         return this.regions[target.toLowerCase()];
32773     },
32774     
32775     onWindowResize : function(){
32776         if(this.monitorWindowResize){
32777             this.layout();
32778         }
32779     }
32780 });/*
32781  * Based on:
32782  * Ext JS Library 1.1.1
32783  * Copyright(c) 2006-2007, Ext JS, LLC.
32784  *
32785  * Originally Released Under LGPL - original licence link has changed is not relivant.
32786  *
32787  * Fork - LGPL
32788  * <script type="text/javascript">
32789  */
32790 /**
32791  * @class Roo.BorderLayout
32792  * @extends Roo.LayoutManager
32793  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32794  * please see: <br><br>
32795  * <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>
32796  * <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>
32797  * Example:
32798  <pre><code>
32799  var layout = new Roo.BorderLayout(document.body, {
32800     north: {
32801         initialSize: 25,
32802         titlebar: false
32803     },
32804     west: {
32805         split:true,
32806         initialSize: 200,
32807         minSize: 175,
32808         maxSize: 400,
32809         titlebar: true,
32810         collapsible: true
32811     },
32812     east: {
32813         split:true,
32814         initialSize: 202,
32815         minSize: 175,
32816         maxSize: 400,
32817         titlebar: true,
32818         collapsible: true
32819     },
32820     south: {
32821         split:true,
32822         initialSize: 100,
32823         minSize: 100,
32824         maxSize: 200,
32825         titlebar: true,
32826         collapsible: true
32827     },
32828     center: {
32829         titlebar: true,
32830         autoScroll:true,
32831         resizeTabs: true,
32832         minTabWidth: 50,
32833         preferredTabWidth: 150
32834     }
32835 });
32836
32837 // shorthand
32838 var CP = Roo.ContentPanel;
32839
32840 layout.beginUpdate();
32841 layout.add("north", new CP("north", "North"));
32842 layout.add("south", new CP("south", {title: "South", closable: true}));
32843 layout.add("west", new CP("west", {title: "West"}));
32844 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32845 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32846 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32847 layout.getRegion("center").showPanel("center1");
32848 layout.endUpdate();
32849 </code></pre>
32850
32851 <b>The container the layout is rendered into can be either the body element or any other element.
32852 If it is not the body element, the container needs to either be an absolute positioned element,
32853 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32854 the container size if it is not the body element.</b>
32855
32856 * @constructor
32857 * Create a new BorderLayout
32858 * @param {String/HTMLElement/Element} container The container this layout is bound to
32859 * @param {Object} config Configuration options
32860  */
32861 Roo.BorderLayout = function(container, config){
32862     config = config || {};
32863     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32864     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32865     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32866         var target = this.factory.validRegions[i];
32867         if(config[target]){
32868             this.addRegion(target, config[target]);
32869         }
32870     }
32871 };
32872
32873 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32874     /**
32875      * Creates and adds a new region if it doesn't already exist.
32876      * @param {String} target The target region key (north, south, east, west or center).
32877      * @param {Object} config The regions config object
32878      * @return {BorderLayoutRegion} The new region
32879      */
32880     addRegion : function(target, config){
32881         if(!this.regions[target]){
32882             var r = this.factory.create(target, this, config);
32883             this.bindRegion(target, r);
32884         }
32885         return this.regions[target];
32886     },
32887
32888     // private (kinda)
32889     bindRegion : function(name, r){
32890         this.regions[name] = r;
32891         r.on("visibilitychange", this.layout, this);
32892         r.on("paneladded", this.layout, this);
32893         r.on("panelremoved", this.layout, this);
32894         r.on("invalidated", this.layout, this);
32895         r.on("resized", this.onRegionResized, this);
32896         r.on("collapsed", this.onRegionCollapsed, this);
32897         r.on("expanded", this.onRegionExpanded, this);
32898     },
32899
32900     /**
32901      * Performs a layout update.
32902      */
32903     layout : function(){
32904         if(this.updating) return;
32905         var size = this.getViewSize();
32906         var w = size.width;
32907         var h = size.height;
32908         var centerW = w;
32909         var centerH = h;
32910         var centerY = 0;
32911         var centerX = 0;
32912         //var x = 0, y = 0;
32913
32914         var rs = this.regions;
32915         var north = rs["north"];
32916         var south = rs["south"]; 
32917         var west = rs["west"];
32918         var east = rs["east"];
32919         var center = rs["center"];
32920         //if(this.hideOnLayout){ // not supported anymore
32921             //c.el.setStyle("display", "none");
32922         //}
32923         if(north && north.isVisible()){
32924             var b = north.getBox();
32925             var m = north.getMargins();
32926             b.width = w - (m.left+m.right);
32927             b.x = m.left;
32928             b.y = m.top;
32929             centerY = b.height + b.y + m.bottom;
32930             centerH -= centerY;
32931             north.updateBox(this.safeBox(b));
32932         }
32933         if(south && south.isVisible()){
32934             var b = south.getBox();
32935             var m = south.getMargins();
32936             b.width = w - (m.left+m.right);
32937             b.x = m.left;
32938             var totalHeight = (b.height + m.top + m.bottom);
32939             b.y = h - totalHeight + m.top;
32940             centerH -= totalHeight;
32941             south.updateBox(this.safeBox(b));
32942         }
32943         if(west && west.isVisible()){
32944             var b = west.getBox();
32945             var m = west.getMargins();
32946             b.height = centerH - (m.top+m.bottom);
32947             b.x = m.left;
32948             b.y = centerY + m.top;
32949             var totalWidth = (b.width + m.left + m.right);
32950             centerX += totalWidth;
32951             centerW -= totalWidth;
32952             west.updateBox(this.safeBox(b));
32953         }
32954         if(east && east.isVisible()){
32955             var b = east.getBox();
32956             var m = east.getMargins();
32957             b.height = centerH - (m.top+m.bottom);
32958             var totalWidth = (b.width + m.left + m.right);
32959             b.x = w - totalWidth + m.left;
32960             b.y = centerY + m.top;
32961             centerW -= totalWidth;
32962             east.updateBox(this.safeBox(b));
32963         }
32964         if(center){
32965             var m = center.getMargins();
32966             var centerBox = {
32967                 x: centerX + m.left,
32968                 y: centerY + m.top,
32969                 width: centerW - (m.left+m.right),
32970                 height: centerH - (m.top+m.bottom)
32971             };
32972             //if(this.hideOnLayout){
32973                 //center.el.setStyle("display", "block");
32974             //}
32975             center.updateBox(this.safeBox(centerBox));
32976         }
32977         this.el.repaint();
32978         this.fireEvent("layout", this);
32979     },
32980
32981     // private
32982     safeBox : function(box){
32983         box.width = Math.max(0, box.width);
32984         box.height = Math.max(0, box.height);
32985         return box;
32986     },
32987
32988     /**
32989      * Adds a ContentPanel (or subclass) to this layout.
32990      * @param {String} target The target region key (north, south, east, west or center).
32991      * @param {Roo.ContentPanel} panel The panel to add
32992      * @return {Roo.ContentPanel} The added panel
32993      */
32994     add : function(target, panel){
32995          
32996         target = target.toLowerCase();
32997         return this.regions[target].add(panel);
32998     },
32999
33000     /**
33001      * Remove a ContentPanel (or subclass) to this layout.
33002      * @param {String} target The target region key (north, south, east, west or center).
33003      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33004      * @return {Roo.ContentPanel} The removed panel
33005      */
33006     remove : function(target, panel){
33007         target = target.toLowerCase();
33008         return this.regions[target].remove(panel);
33009     },
33010
33011     /**
33012      * Searches all regions for a panel with the specified id
33013      * @param {String} panelId
33014      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33015      */
33016     findPanel : function(panelId){
33017         var rs = this.regions;
33018         for(var target in rs){
33019             if(typeof rs[target] != "function"){
33020                 var p = rs[target].getPanel(panelId);
33021                 if(p){
33022                     return p;
33023                 }
33024             }
33025         }
33026         return null;
33027     },
33028
33029     /**
33030      * Searches all regions for a panel with the specified id and activates (shows) it.
33031      * @param {String/ContentPanel} panelId The panels id or the panel itself
33032      * @return {Roo.ContentPanel} The shown panel or null
33033      */
33034     showPanel : function(panelId) {
33035       var rs = this.regions;
33036       for(var target in rs){
33037          var r = rs[target];
33038          if(typeof r != "function"){
33039             if(r.hasPanel(panelId)){
33040                return r.showPanel(panelId);
33041             }
33042          }
33043       }
33044       return null;
33045    },
33046
33047    /**
33048      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33049      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33050      */
33051     restoreState : function(provider){
33052         if(!provider){
33053             provider = Roo.state.Manager;
33054         }
33055         var sm = new Roo.LayoutStateManager();
33056         sm.init(this, provider);
33057     },
33058
33059     /**
33060      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33061      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33062      * a valid ContentPanel config object.  Example:
33063      * <pre><code>
33064 // Create the main layout
33065 var layout = new Roo.BorderLayout('main-ct', {
33066     west: {
33067         split:true,
33068         minSize: 175,
33069         titlebar: true
33070     },
33071     center: {
33072         title:'Components'
33073     }
33074 }, 'main-ct');
33075
33076 // Create and add multiple ContentPanels at once via configs
33077 layout.batchAdd({
33078    west: {
33079        id: 'source-files',
33080        autoCreate:true,
33081        title:'Ext Source Files',
33082        autoScroll:true,
33083        fitToFrame:true
33084    },
33085    center : {
33086        el: cview,
33087        autoScroll:true,
33088        fitToFrame:true,
33089        toolbar: tb,
33090        resizeEl:'cbody'
33091    }
33092 });
33093 </code></pre>
33094      * @param {Object} regions An object containing ContentPanel configs by region name
33095      */
33096     batchAdd : function(regions){
33097         this.beginUpdate();
33098         for(var rname in regions){
33099             var lr = this.regions[rname];
33100             if(lr){
33101                 this.addTypedPanels(lr, regions[rname]);
33102             }
33103         }
33104         this.endUpdate();
33105     },
33106
33107     // private
33108     addTypedPanels : function(lr, ps){
33109         if(typeof ps == 'string'){
33110             lr.add(new Roo.ContentPanel(ps));
33111         }
33112         else if(ps instanceof Array){
33113             for(var i =0, len = ps.length; i < len; i++){
33114                 this.addTypedPanels(lr, ps[i]);
33115             }
33116         }
33117         else if(!ps.events){ // raw config?
33118             var el = ps.el;
33119             delete ps.el; // prevent conflict
33120             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33121         }
33122         else {  // panel object assumed!
33123             lr.add(ps);
33124         }
33125     },
33126     /**
33127      * Adds a xtype elements to the layout.
33128      * <pre><code>
33129
33130 layout.addxtype({
33131        xtype : 'ContentPanel',
33132        region: 'west',
33133        items: [ .... ]
33134    }
33135 );
33136
33137 layout.addxtype({
33138         xtype : 'NestedLayoutPanel',
33139         region: 'west',
33140         layout: {
33141            center: { },
33142            west: { }   
33143         },
33144         items : [ ... list of content panels or nested layout panels.. ]
33145    }
33146 );
33147 </code></pre>
33148      * @param {Object} cfg Xtype definition of item to add.
33149      */
33150     addxtype : function(cfg)
33151     {
33152         // basically accepts a pannel...
33153         // can accept a layout region..!?!?
33154         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33155         
33156         if (!cfg.xtype.match(/Panel$/)) {
33157             return false;
33158         }
33159         var ret = false;
33160         
33161         if (typeof(cfg.region) == 'undefined') {
33162             Roo.log("Failed to add Panel, region was not set");
33163             Roo.log(cfg);
33164             return false;
33165         }
33166         var region = cfg.region;
33167         delete cfg.region;
33168         
33169           
33170         var xitems = [];
33171         if (cfg.items) {
33172             xitems = cfg.items;
33173             delete cfg.items;
33174         }
33175         var nb = false;
33176         
33177         switch(cfg.xtype) 
33178         {
33179             case 'ContentPanel':  // ContentPanel (el, cfg)
33180             case 'ScrollPanel':  // ContentPanel (el, cfg)
33181             case 'ViewPanel': 
33182                 if(cfg.autoCreate) {
33183                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33184                 } else {
33185                     var el = this.el.createChild();
33186                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33187                 }
33188                 
33189                 this.add(region, ret);
33190                 break;
33191             
33192             
33193             case 'TreePanel': // our new panel!
33194                 cfg.el = this.el.createChild();
33195                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33196                 this.add(region, ret);
33197                 break;
33198             
33199             case 'NestedLayoutPanel': 
33200                 // create a new Layout (which is  a Border Layout...
33201                 var el = this.el.createChild();
33202                 var clayout = cfg.layout;
33203                 delete cfg.layout;
33204                 clayout.items   = clayout.items  || [];
33205                 // replace this exitems with the clayout ones..
33206                 xitems = clayout.items;
33207                  
33208                 
33209                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33210                     cfg.background = false;
33211                 }
33212                 var layout = new Roo.BorderLayout(el, clayout);
33213                 
33214                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33215                 //console.log('adding nested layout panel '  + cfg.toSource());
33216                 this.add(region, ret);
33217                 nb = {}; /// find first...
33218                 break;
33219                 
33220             case 'GridPanel': 
33221             
33222                 // needs grid and region
33223                 
33224                 //var el = this.getRegion(region).el.createChild();
33225                 var el = this.el.createChild();
33226                 // create the grid first...
33227                 
33228                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33229                 delete cfg.grid;
33230                 if (region == 'center' && this.active ) {
33231                     cfg.background = false;
33232                 }
33233                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33234                 
33235                 this.add(region, ret);
33236                 if (cfg.background) {
33237                     ret.on('activate', function(gp) {
33238                         if (!gp.grid.rendered) {
33239                             gp.grid.render();
33240                         }
33241                     });
33242                 } else {
33243                     grid.render();
33244                 }
33245                 break;
33246            
33247            
33248            
33249                 
33250                 
33251                 
33252             default:
33253                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33254                     
33255                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33256                     this.add(region, ret);
33257                 } else {
33258                 
33259                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33260                     return null;
33261                 }
33262                 
33263              // GridPanel (grid, cfg)
33264             
33265         }
33266         this.beginUpdate();
33267         // add children..
33268         var region = '';
33269         var abn = {};
33270         Roo.each(xitems, function(i)  {
33271             region = nb && i.region ? i.region : false;
33272             
33273             var add = ret.addxtype(i);
33274            
33275             if (region) {
33276                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33277                 if (!i.background) {
33278                     abn[region] = nb[region] ;
33279                 }
33280             }
33281             
33282         });
33283         this.endUpdate();
33284
33285         // make the last non-background panel active..
33286         //if (nb) { Roo.log(abn); }
33287         if (nb) {
33288             
33289             for(var r in abn) {
33290                 region = this.getRegion(r);
33291                 if (region) {
33292                     // tried using nb[r], but it does not work..
33293                      
33294                     region.showPanel(abn[r]);
33295                    
33296                 }
33297             }
33298         }
33299         return ret;
33300         
33301     }
33302 });
33303
33304 /**
33305  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33306  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33307  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33308  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33309  * <pre><code>
33310 // shorthand
33311 var CP = Roo.ContentPanel;
33312
33313 var layout = Roo.BorderLayout.create({
33314     north: {
33315         initialSize: 25,
33316         titlebar: false,
33317         panels: [new CP("north", "North")]
33318     },
33319     west: {
33320         split:true,
33321         initialSize: 200,
33322         minSize: 175,
33323         maxSize: 400,
33324         titlebar: true,
33325         collapsible: true,
33326         panels: [new CP("west", {title: "West"})]
33327     },
33328     east: {
33329         split:true,
33330         initialSize: 202,
33331         minSize: 175,
33332         maxSize: 400,
33333         titlebar: true,
33334         collapsible: true,
33335         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33336     },
33337     south: {
33338         split:true,
33339         initialSize: 100,
33340         minSize: 100,
33341         maxSize: 200,
33342         titlebar: true,
33343         collapsible: true,
33344         panels: [new CP("south", {title: "South", closable: true})]
33345     },
33346     center: {
33347         titlebar: true,
33348         autoScroll:true,
33349         resizeTabs: true,
33350         minTabWidth: 50,
33351         preferredTabWidth: 150,
33352         panels: [
33353             new CP("center1", {title: "Close Me", closable: true}),
33354             new CP("center2", {title: "Center Panel", closable: false})
33355         ]
33356     }
33357 }, document.body);
33358
33359 layout.getRegion("center").showPanel("center1");
33360 </code></pre>
33361  * @param config
33362  * @param targetEl
33363  */
33364 Roo.BorderLayout.create = function(config, targetEl){
33365     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33366     layout.beginUpdate();
33367     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33368     for(var j = 0, jlen = regions.length; j < jlen; j++){
33369         var lr = regions[j];
33370         if(layout.regions[lr] && config[lr].panels){
33371             var r = layout.regions[lr];
33372             var ps = config[lr].panels;
33373             layout.addTypedPanels(r, ps);
33374         }
33375     }
33376     layout.endUpdate();
33377     return layout;
33378 };
33379
33380 // private
33381 Roo.BorderLayout.RegionFactory = {
33382     // private
33383     validRegions : ["north","south","east","west","center"],
33384
33385     // private
33386     create : function(target, mgr, config){
33387         target = target.toLowerCase();
33388         if(config.lightweight || config.basic){
33389             return new Roo.BasicLayoutRegion(mgr, config, target);
33390         }
33391         switch(target){
33392             case "north":
33393                 return new Roo.NorthLayoutRegion(mgr, config);
33394             case "south":
33395                 return new Roo.SouthLayoutRegion(mgr, config);
33396             case "east":
33397                 return new Roo.EastLayoutRegion(mgr, config);
33398             case "west":
33399                 return new Roo.WestLayoutRegion(mgr, config);
33400             case "center":
33401                 return new Roo.CenterLayoutRegion(mgr, config);
33402         }
33403         throw 'Layout region "'+target+'" not supported.';
33404     }
33405 };/*
33406  * Based on:
33407  * Ext JS Library 1.1.1
33408  * Copyright(c) 2006-2007, Ext JS, LLC.
33409  *
33410  * Originally Released Under LGPL - original licence link has changed is not relivant.
33411  *
33412  * Fork - LGPL
33413  * <script type="text/javascript">
33414  */
33415  
33416 /**
33417  * @class Roo.BasicLayoutRegion
33418  * @extends Roo.util.Observable
33419  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33420  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33421  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33422  */
33423 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33424     this.mgr = mgr;
33425     this.position  = pos;
33426     this.events = {
33427         /**
33428          * @scope Roo.BasicLayoutRegion
33429          */
33430         
33431         /**
33432          * @event beforeremove
33433          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33434          * @param {Roo.LayoutRegion} this
33435          * @param {Roo.ContentPanel} panel The panel
33436          * @param {Object} e The cancel event object
33437          */
33438         "beforeremove" : true,
33439         /**
33440          * @event invalidated
33441          * Fires when the layout for this region is changed.
33442          * @param {Roo.LayoutRegion} this
33443          */
33444         "invalidated" : true,
33445         /**
33446          * @event visibilitychange
33447          * Fires when this region is shown or hidden 
33448          * @param {Roo.LayoutRegion} this
33449          * @param {Boolean} visibility true or false
33450          */
33451         "visibilitychange" : true,
33452         /**
33453          * @event paneladded
33454          * Fires when a panel is added. 
33455          * @param {Roo.LayoutRegion} this
33456          * @param {Roo.ContentPanel} panel The panel
33457          */
33458         "paneladded" : true,
33459         /**
33460          * @event panelremoved
33461          * Fires when a panel is removed. 
33462          * @param {Roo.LayoutRegion} this
33463          * @param {Roo.ContentPanel} panel The panel
33464          */
33465         "panelremoved" : true,
33466         /**
33467          * @event collapsed
33468          * Fires when this region is collapsed.
33469          * @param {Roo.LayoutRegion} this
33470          */
33471         "collapsed" : true,
33472         /**
33473          * @event expanded
33474          * Fires when this region is expanded.
33475          * @param {Roo.LayoutRegion} this
33476          */
33477         "expanded" : true,
33478         /**
33479          * @event slideshow
33480          * Fires when this region is slid into view.
33481          * @param {Roo.LayoutRegion} this
33482          */
33483         "slideshow" : true,
33484         /**
33485          * @event slidehide
33486          * Fires when this region slides out of view. 
33487          * @param {Roo.LayoutRegion} this
33488          */
33489         "slidehide" : true,
33490         /**
33491          * @event panelactivated
33492          * Fires when a panel is activated. 
33493          * @param {Roo.LayoutRegion} this
33494          * @param {Roo.ContentPanel} panel The activated panel
33495          */
33496         "panelactivated" : true,
33497         /**
33498          * @event resized
33499          * Fires when the user resizes this region. 
33500          * @param {Roo.LayoutRegion} this
33501          * @param {Number} newSize The new size (width for east/west, height for north/south)
33502          */
33503         "resized" : true
33504     };
33505     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33506     this.panels = new Roo.util.MixedCollection();
33507     this.panels.getKey = this.getPanelId.createDelegate(this);
33508     this.box = null;
33509     this.activePanel = null;
33510     // ensure listeners are added...
33511     
33512     if (config.listeners || config.events) {
33513         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33514             listeners : config.listeners || {},
33515             events : config.events || {}
33516         });
33517     }
33518     
33519     if(skipConfig !== true){
33520         this.applyConfig(config);
33521     }
33522 };
33523
33524 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33525     getPanelId : function(p){
33526         return p.getId();
33527     },
33528     
33529     applyConfig : function(config){
33530         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33531         this.config = config;
33532         
33533     },
33534     
33535     /**
33536      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33537      * the width, for horizontal (north, south) the height.
33538      * @param {Number} newSize The new width or height
33539      */
33540     resizeTo : function(newSize){
33541         var el = this.el ? this.el :
33542                  (this.activePanel ? this.activePanel.getEl() : null);
33543         if(el){
33544             switch(this.position){
33545                 case "east":
33546                 case "west":
33547                     el.setWidth(newSize);
33548                     this.fireEvent("resized", this, newSize);
33549                 break;
33550                 case "north":
33551                 case "south":
33552                     el.setHeight(newSize);
33553                     this.fireEvent("resized", this, newSize);
33554                 break;                
33555             }
33556         }
33557     },
33558     
33559     getBox : function(){
33560         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33561     },
33562     
33563     getMargins : function(){
33564         return this.margins;
33565     },
33566     
33567     updateBox : function(box){
33568         this.box = box;
33569         var el = this.activePanel.getEl();
33570         el.dom.style.left = box.x + "px";
33571         el.dom.style.top = box.y + "px";
33572         this.activePanel.setSize(box.width, box.height);
33573     },
33574     
33575     /**
33576      * Returns the container element for this region.
33577      * @return {Roo.Element}
33578      */
33579     getEl : function(){
33580         return this.activePanel;
33581     },
33582     
33583     /**
33584      * Returns true if this region is currently visible.
33585      * @return {Boolean}
33586      */
33587     isVisible : function(){
33588         return this.activePanel ? true : false;
33589     },
33590     
33591     setActivePanel : function(panel){
33592         panel = this.getPanel(panel);
33593         if(this.activePanel && this.activePanel != panel){
33594             this.activePanel.setActiveState(false);
33595             this.activePanel.getEl().setLeftTop(-10000,-10000);
33596         }
33597         this.activePanel = panel;
33598         panel.setActiveState(true);
33599         if(this.box){
33600             panel.setSize(this.box.width, this.box.height);
33601         }
33602         this.fireEvent("panelactivated", this, panel);
33603         this.fireEvent("invalidated");
33604     },
33605     
33606     /**
33607      * Show the specified panel.
33608      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33609      * @return {Roo.ContentPanel} The shown panel or null
33610      */
33611     showPanel : function(panel){
33612         if(panel = this.getPanel(panel)){
33613             this.setActivePanel(panel);
33614         }
33615         return panel;
33616     },
33617     
33618     /**
33619      * Get the active panel for this region.
33620      * @return {Roo.ContentPanel} The active panel or null
33621      */
33622     getActivePanel : function(){
33623         return this.activePanel;
33624     },
33625     
33626     /**
33627      * Add the passed ContentPanel(s)
33628      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33629      * @return {Roo.ContentPanel} The panel added (if only one was added)
33630      */
33631     add : function(panel){
33632         if(arguments.length > 1){
33633             for(var i = 0, len = arguments.length; i < len; i++) {
33634                 this.add(arguments[i]);
33635             }
33636             return null;
33637         }
33638         if(this.hasPanel(panel)){
33639             this.showPanel(panel);
33640             return panel;
33641         }
33642         var el = panel.getEl();
33643         if(el.dom.parentNode != this.mgr.el.dom){
33644             this.mgr.el.dom.appendChild(el.dom);
33645         }
33646         if(panel.setRegion){
33647             panel.setRegion(this);
33648         }
33649         this.panels.add(panel);
33650         el.setStyle("position", "absolute");
33651         if(!panel.background){
33652             this.setActivePanel(panel);
33653             if(this.config.initialSize && this.panels.getCount()==1){
33654                 this.resizeTo(this.config.initialSize);
33655             }
33656         }
33657         this.fireEvent("paneladded", this, panel);
33658         return panel;
33659     },
33660     
33661     /**
33662      * Returns true if the panel is in this region.
33663      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33664      * @return {Boolean}
33665      */
33666     hasPanel : function(panel){
33667         if(typeof panel == "object"){ // must be panel obj
33668             panel = panel.getId();
33669         }
33670         return this.getPanel(panel) ? true : false;
33671     },
33672     
33673     /**
33674      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33675      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33676      * @param {Boolean} preservePanel Overrides the config preservePanel option
33677      * @return {Roo.ContentPanel} The panel that was removed
33678      */
33679     remove : function(panel, preservePanel){
33680         panel = this.getPanel(panel);
33681         if(!panel){
33682             return null;
33683         }
33684         var e = {};
33685         this.fireEvent("beforeremove", this, panel, e);
33686         if(e.cancel === true){
33687             return null;
33688         }
33689         var panelId = panel.getId();
33690         this.panels.removeKey(panelId);
33691         return panel;
33692     },
33693     
33694     /**
33695      * Returns the panel specified or null if it's not in this region.
33696      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33697      * @return {Roo.ContentPanel}
33698      */
33699     getPanel : function(id){
33700         if(typeof id == "object"){ // must be panel obj
33701             return id;
33702         }
33703         return this.panels.get(id);
33704     },
33705     
33706     /**
33707      * Returns this regions position (north/south/east/west/center).
33708      * @return {String} 
33709      */
33710     getPosition: function(){
33711         return this.position;    
33712     }
33713 });/*
33714  * Based on:
33715  * Ext JS Library 1.1.1
33716  * Copyright(c) 2006-2007, Ext JS, LLC.
33717  *
33718  * Originally Released Under LGPL - original licence link has changed is not relivant.
33719  *
33720  * Fork - LGPL
33721  * <script type="text/javascript">
33722  */
33723  
33724 /**
33725  * @class Roo.LayoutRegion
33726  * @extends Roo.BasicLayoutRegion
33727  * This class represents a region in a layout manager.
33728  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33729  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33730  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33731  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33732  * @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})
33733  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33734  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33735  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33736  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33737  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33738  * @cfg {String}    title           The title for the region (overrides panel titles)
33739  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33740  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33741  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33742  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33743  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33744  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33745  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33746  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33747  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33748  * @cfg {Boolean}   showPin         True to show a pin button
33749  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33750  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33751  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33752  * @cfg {Number}    width           For East/West panels
33753  * @cfg {Number}    height          For North/South panels
33754  * @cfg {Boolean}   split           To show the splitter
33755  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33756  */
33757 Roo.LayoutRegion = function(mgr, config, pos){
33758     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33759     var dh = Roo.DomHelper;
33760     /** This region's container element 
33761     * @type Roo.Element */
33762     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33763     /** This region's title element 
33764     * @type Roo.Element */
33765
33766     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33767         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33768         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33769     ]}, true);
33770     this.titleEl.enableDisplayMode();
33771     /** This region's title text element 
33772     * @type HTMLElement */
33773     this.titleTextEl = this.titleEl.dom.firstChild;
33774     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33775     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33776     this.closeBtn.enableDisplayMode();
33777     this.closeBtn.on("click", this.closeClicked, this);
33778     this.closeBtn.hide();
33779
33780     this.createBody(config);
33781     this.visible = true;
33782     this.collapsed = false;
33783
33784     if(config.hideWhenEmpty){
33785         this.hide();
33786         this.on("paneladded", this.validateVisibility, this);
33787         this.on("panelremoved", this.validateVisibility, this);
33788     }
33789     this.applyConfig(config);
33790 };
33791
33792 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33793
33794     createBody : function(){
33795         /** This region's body element 
33796         * @type Roo.Element */
33797         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33798     },
33799
33800     applyConfig : function(c){
33801         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33802             var dh = Roo.DomHelper;
33803             if(c.titlebar !== false){
33804                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33805                 this.collapseBtn.on("click", this.collapse, this);
33806                 this.collapseBtn.enableDisplayMode();
33807
33808                 if(c.showPin === true || this.showPin){
33809                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33810                     this.stickBtn.enableDisplayMode();
33811                     this.stickBtn.on("click", this.expand, this);
33812                     this.stickBtn.hide();
33813                 }
33814             }
33815             /** This region's collapsed element
33816             * @type Roo.Element */
33817             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33818                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33819             ]}, true);
33820             if(c.floatable !== false){
33821                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33822                this.collapsedEl.on("click", this.collapseClick, this);
33823             }
33824
33825             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33826                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33827                    id: "message", unselectable: "on", style:{"float":"left"}});
33828                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33829              }
33830             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33831             this.expandBtn.on("click", this.expand, this);
33832         }
33833         if(this.collapseBtn){
33834             this.collapseBtn.setVisible(c.collapsible == true);
33835         }
33836         this.cmargins = c.cmargins || this.cmargins ||
33837                          (this.position == "west" || this.position == "east" ?
33838                              {top: 0, left: 2, right:2, bottom: 0} :
33839                              {top: 2, left: 0, right:0, bottom: 2});
33840         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33841         this.bottomTabs = c.tabPosition != "top";
33842         this.autoScroll = c.autoScroll || false;
33843         if(this.autoScroll){
33844             this.bodyEl.setStyle("overflow", "auto");
33845         }else{
33846             this.bodyEl.setStyle("overflow", "hidden");
33847         }
33848         //if(c.titlebar !== false){
33849             if((!c.titlebar && !c.title) || c.titlebar === false){
33850                 this.titleEl.hide();
33851             }else{
33852                 this.titleEl.show();
33853                 if(c.title){
33854                     this.titleTextEl.innerHTML = c.title;
33855                 }
33856             }
33857         //}
33858         this.duration = c.duration || .30;
33859         this.slideDuration = c.slideDuration || .45;
33860         this.config = c;
33861         if(c.collapsed){
33862             this.collapse(true);
33863         }
33864         if(c.hidden){
33865             this.hide();
33866         }
33867     },
33868     /**
33869      * Returns true if this region is currently visible.
33870      * @return {Boolean}
33871      */
33872     isVisible : function(){
33873         return this.visible;
33874     },
33875
33876     /**
33877      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33878      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33879      */
33880     setCollapsedTitle : function(title){
33881         title = title || "&#160;";
33882         if(this.collapsedTitleTextEl){
33883             this.collapsedTitleTextEl.innerHTML = title;
33884         }
33885     },
33886
33887     getBox : function(){
33888         var b;
33889         if(!this.collapsed){
33890             b = this.el.getBox(false, true);
33891         }else{
33892             b = this.collapsedEl.getBox(false, true);
33893         }
33894         return b;
33895     },
33896
33897     getMargins : function(){
33898         return this.collapsed ? this.cmargins : this.margins;
33899     },
33900
33901     highlight : function(){
33902         this.el.addClass("x-layout-panel-dragover");
33903     },
33904
33905     unhighlight : function(){
33906         this.el.removeClass("x-layout-panel-dragover");
33907     },
33908
33909     updateBox : function(box){
33910         this.box = box;
33911         if(!this.collapsed){
33912             this.el.dom.style.left = box.x + "px";
33913             this.el.dom.style.top = box.y + "px";
33914             this.updateBody(box.width, box.height);
33915         }else{
33916             this.collapsedEl.dom.style.left = box.x + "px";
33917             this.collapsedEl.dom.style.top = box.y + "px";
33918             this.collapsedEl.setSize(box.width, box.height);
33919         }
33920         if(this.tabs){
33921             this.tabs.autoSizeTabs();
33922         }
33923     },
33924
33925     updateBody : function(w, h){
33926         if(w !== null){
33927             this.el.setWidth(w);
33928             w -= this.el.getBorderWidth("rl");
33929             if(this.config.adjustments){
33930                 w += this.config.adjustments[0];
33931             }
33932         }
33933         if(h !== null){
33934             this.el.setHeight(h);
33935             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33936             h -= this.el.getBorderWidth("tb");
33937             if(this.config.adjustments){
33938                 h += this.config.adjustments[1];
33939             }
33940             this.bodyEl.setHeight(h);
33941             if(this.tabs){
33942                 h = this.tabs.syncHeight(h);
33943             }
33944         }
33945         if(this.panelSize){
33946             w = w !== null ? w : this.panelSize.width;
33947             h = h !== null ? h : this.panelSize.height;
33948         }
33949         if(this.activePanel){
33950             var el = this.activePanel.getEl();
33951             w = w !== null ? w : el.getWidth();
33952             h = h !== null ? h : el.getHeight();
33953             this.panelSize = {width: w, height: h};
33954             this.activePanel.setSize(w, h);
33955         }
33956         if(Roo.isIE && this.tabs){
33957             this.tabs.el.repaint();
33958         }
33959     },
33960
33961     /**
33962      * Returns the container element for this region.
33963      * @return {Roo.Element}
33964      */
33965     getEl : function(){
33966         return this.el;
33967     },
33968
33969     /**
33970      * Hides this region.
33971      */
33972     hide : function(){
33973         if(!this.collapsed){
33974             this.el.dom.style.left = "-2000px";
33975             this.el.hide();
33976         }else{
33977             this.collapsedEl.dom.style.left = "-2000px";
33978             this.collapsedEl.hide();
33979         }
33980         this.visible = false;
33981         this.fireEvent("visibilitychange", this, false);
33982     },
33983
33984     /**
33985      * Shows this region if it was previously hidden.
33986      */
33987     show : function(){
33988         if(!this.collapsed){
33989             this.el.show();
33990         }else{
33991             this.collapsedEl.show();
33992         }
33993         this.visible = true;
33994         this.fireEvent("visibilitychange", this, true);
33995     },
33996
33997     closeClicked : function(){
33998         if(this.activePanel){
33999             this.remove(this.activePanel);
34000         }
34001     },
34002
34003     collapseClick : function(e){
34004         if(this.isSlid){
34005            e.stopPropagation();
34006            this.slideIn();
34007         }else{
34008            e.stopPropagation();
34009            this.slideOut();
34010         }
34011     },
34012
34013     /**
34014      * Collapses this region.
34015      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34016      */
34017     collapse : function(skipAnim){
34018         if(this.collapsed) return;
34019         this.collapsed = true;
34020         if(this.split){
34021             this.split.el.hide();
34022         }
34023         if(this.config.animate && skipAnim !== true){
34024             this.fireEvent("invalidated", this);
34025             this.animateCollapse();
34026         }else{
34027             this.el.setLocation(-20000,-20000);
34028             this.el.hide();
34029             this.collapsedEl.show();
34030             this.fireEvent("collapsed", this);
34031             this.fireEvent("invalidated", this);
34032         }
34033     },
34034
34035     animateCollapse : function(){
34036         // overridden
34037     },
34038
34039     /**
34040      * Expands this region if it was previously collapsed.
34041      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34042      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34043      */
34044     expand : function(e, skipAnim){
34045         if(e) e.stopPropagation();
34046         if(!this.collapsed || this.el.hasActiveFx()) return;
34047         if(this.isSlid){
34048             this.afterSlideIn();
34049             skipAnim = true;
34050         }
34051         this.collapsed = false;
34052         if(this.config.animate && skipAnim !== true){
34053             this.animateExpand();
34054         }else{
34055             this.el.show();
34056             if(this.split){
34057                 this.split.el.show();
34058             }
34059             this.collapsedEl.setLocation(-2000,-2000);
34060             this.collapsedEl.hide();
34061             this.fireEvent("invalidated", this);
34062             this.fireEvent("expanded", this);
34063         }
34064     },
34065
34066     animateExpand : function(){
34067         // overridden
34068     },
34069
34070     initTabs : function()
34071     {
34072         this.bodyEl.setStyle("overflow", "hidden");
34073         var ts = new Roo.TabPanel(
34074                 this.bodyEl.dom,
34075                 {
34076                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34077                     disableTooltips: this.config.disableTabTips,
34078                     toolbar : this.config.toolbar
34079                 }
34080         );
34081         if(this.config.hideTabs){
34082             ts.stripWrap.setDisplayed(false);
34083         }
34084         this.tabs = ts;
34085         ts.resizeTabs = this.config.resizeTabs === true;
34086         ts.minTabWidth = this.config.minTabWidth || 40;
34087         ts.maxTabWidth = this.config.maxTabWidth || 250;
34088         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34089         ts.monitorResize = false;
34090         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34091         ts.bodyEl.addClass('x-layout-tabs-body');
34092         this.panels.each(this.initPanelAsTab, this);
34093     },
34094
34095     initPanelAsTab : function(panel){
34096         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34097                     this.config.closeOnTab && panel.isClosable());
34098         if(panel.tabTip !== undefined){
34099             ti.setTooltip(panel.tabTip);
34100         }
34101         ti.on("activate", function(){
34102               this.setActivePanel(panel);
34103         }, this);
34104         if(this.config.closeOnTab){
34105             ti.on("beforeclose", function(t, e){
34106                 e.cancel = true;
34107                 this.remove(panel);
34108             }, this);
34109         }
34110         return ti;
34111     },
34112
34113     updatePanelTitle : function(panel, title){
34114         if(this.activePanel == panel){
34115             this.updateTitle(title);
34116         }
34117         if(this.tabs){
34118             var ti = this.tabs.getTab(panel.getEl().id);
34119             ti.setText(title);
34120             if(panel.tabTip !== undefined){
34121                 ti.setTooltip(panel.tabTip);
34122             }
34123         }
34124     },
34125
34126     updateTitle : function(title){
34127         if(this.titleTextEl && !this.config.title){
34128             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34129         }
34130     },
34131
34132     setActivePanel : function(panel){
34133         panel = this.getPanel(panel);
34134         if(this.activePanel && this.activePanel != panel){
34135             this.activePanel.setActiveState(false);
34136         }
34137         this.activePanel = panel;
34138         panel.setActiveState(true);
34139         if(this.panelSize){
34140             panel.setSize(this.panelSize.width, this.panelSize.height);
34141         }
34142         if(this.closeBtn){
34143             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34144         }
34145         this.updateTitle(panel.getTitle());
34146         if(this.tabs){
34147             this.fireEvent("invalidated", this);
34148         }
34149         this.fireEvent("panelactivated", this, panel);
34150     },
34151
34152     /**
34153      * Shows the specified panel.
34154      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34155      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34156      */
34157     showPanel : function(panel)
34158     {
34159         panel = this.getPanel(panel);
34160         if(panel){
34161             if(this.tabs){
34162                 var tab = this.tabs.getTab(panel.getEl().id);
34163                 if(tab.isHidden()){
34164                     this.tabs.unhideTab(tab.id);
34165                 }
34166                 tab.activate();
34167             }else{
34168                 this.setActivePanel(panel);
34169             }
34170         }
34171         return panel;
34172     },
34173
34174     /**
34175      * Get the active panel for this region.
34176      * @return {Roo.ContentPanel} The active panel or null
34177      */
34178     getActivePanel : function(){
34179         return this.activePanel;
34180     },
34181
34182     validateVisibility : function(){
34183         if(this.panels.getCount() < 1){
34184             this.updateTitle("&#160;");
34185             this.closeBtn.hide();
34186             this.hide();
34187         }else{
34188             if(!this.isVisible()){
34189                 this.show();
34190             }
34191         }
34192     },
34193
34194     /**
34195      * Adds the passed ContentPanel(s) to this region.
34196      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34197      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34198      */
34199     add : function(panel){
34200         if(arguments.length > 1){
34201             for(var i = 0, len = arguments.length; i < len; i++) {
34202                 this.add(arguments[i]);
34203             }
34204             return null;
34205         }
34206         if(this.hasPanel(panel)){
34207             this.showPanel(panel);
34208             return panel;
34209         }
34210         panel.setRegion(this);
34211         this.panels.add(panel);
34212         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34213             this.bodyEl.dom.appendChild(panel.getEl().dom);
34214             if(panel.background !== true){
34215                 this.setActivePanel(panel);
34216             }
34217             this.fireEvent("paneladded", this, panel);
34218             return panel;
34219         }
34220         if(!this.tabs){
34221             this.initTabs();
34222         }else{
34223             this.initPanelAsTab(panel);
34224         }
34225         if(panel.background !== true){
34226             this.tabs.activate(panel.getEl().id);
34227         }
34228         this.fireEvent("paneladded", this, panel);
34229         return panel;
34230     },
34231
34232     /**
34233      * Hides the tab for the specified panel.
34234      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34235      */
34236     hidePanel : function(panel){
34237         if(this.tabs && (panel = this.getPanel(panel))){
34238             this.tabs.hideTab(panel.getEl().id);
34239         }
34240     },
34241
34242     /**
34243      * Unhides the tab for a previously hidden panel.
34244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34245      */
34246     unhidePanel : function(panel){
34247         if(this.tabs && (panel = this.getPanel(panel))){
34248             this.tabs.unhideTab(panel.getEl().id);
34249         }
34250     },
34251
34252     clearPanels : function(){
34253         while(this.panels.getCount() > 0){
34254              this.remove(this.panels.first());
34255         }
34256     },
34257
34258     /**
34259      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34260      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34261      * @param {Boolean} preservePanel Overrides the config preservePanel option
34262      * @return {Roo.ContentPanel} The panel that was removed
34263      */
34264     remove : function(panel, preservePanel){
34265         panel = this.getPanel(panel);
34266         if(!panel){
34267             return null;
34268         }
34269         var e = {};
34270         this.fireEvent("beforeremove", this, panel, e);
34271         if(e.cancel === true){
34272             return null;
34273         }
34274         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34275         var panelId = panel.getId();
34276         this.panels.removeKey(panelId);
34277         if(preservePanel){
34278             document.body.appendChild(panel.getEl().dom);
34279         }
34280         if(this.tabs){
34281             this.tabs.removeTab(panel.getEl().id);
34282         }else if (!preservePanel){
34283             this.bodyEl.dom.removeChild(panel.getEl().dom);
34284         }
34285         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34286             var p = this.panels.first();
34287             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34288             tempEl.appendChild(p.getEl().dom);
34289             this.bodyEl.update("");
34290             this.bodyEl.dom.appendChild(p.getEl().dom);
34291             tempEl = null;
34292             this.updateTitle(p.getTitle());
34293             this.tabs = null;
34294             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34295             this.setActivePanel(p);
34296         }
34297         panel.setRegion(null);
34298         if(this.activePanel == panel){
34299             this.activePanel = null;
34300         }
34301         if(this.config.autoDestroy !== false && preservePanel !== true){
34302             try{panel.destroy();}catch(e){}
34303         }
34304         this.fireEvent("panelremoved", this, panel);
34305         return panel;
34306     },
34307
34308     /**
34309      * Returns the TabPanel component used by this region
34310      * @return {Roo.TabPanel}
34311      */
34312     getTabs : function(){
34313         return this.tabs;
34314     },
34315
34316     createTool : function(parentEl, className){
34317         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34318             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34319         btn.addClassOnOver("x-layout-tools-button-over");
34320         return btn;
34321     }
34322 });/*
34323  * Based on:
34324  * Ext JS Library 1.1.1
34325  * Copyright(c) 2006-2007, Ext JS, LLC.
34326  *
34327  * Originally Released Under LGPL - original licence link has changed is not relivant.
34328  *
34329  * Fork - LGPL
34330  * <script type="text/javascript">
34331  */
34332  
34333
34334
34335 /**
34336  * @class Roo.SplitLayoutRegion
34337  * @extends Roo.LayoutRegion
34338  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34339  */
34340 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34341     this.cursor = cursor;
34342     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34343 };
34344
34345 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34346     splitTip : "Drag to resize.",
34347     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34348     useSplitTips : false,
34349
34350     applyConfig : function(config){
34351         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34352         if(config.split){
34353             if(!this.split){
34354                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34355                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34356                 /** The SplitBar for this region 
34357                 * @type Roo.SplitBar */
34358                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34359                 this.split.on("moved", this.onSplitMove, this);
34360                 this.split.useShim = config.useShim === true;
34361                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34362                 if(this.useSplitTips){
34363                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34364                 }
34365                 if(config.collapsible){
34366                     this.split.el.on("dblclick", this.collapse,  this);
34367                 }
34368             }
34369             if(typeof config.minSize != "undefined"){
34370                 this.split.minSize = config.minSize;
34371             }
34372             if(typeof config.maxSize != "undefined"){
34373                 this.split.maxSize = config.maxSize;
34374             }
34375             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34376                 this.hideSplitter();
34377             }
34378         }
34379     },
34380
34381     getHMaxSize : function(){
34382          var cmax = this.config.maxSize || 10000;
34383          var center = this.mgr.getRegion("center");
34384          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34385     },
34386
34387     getVMaxSize : function(){
34388          var cmax = this.config.maxSize || 10000;
34389          var center = this.mgr.getRegion("center");
34390          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34391     },
34392
34393     onSplitMove : function(split, newSize){
34394         this.fireEvent("resized", this, newSize);
34395     },
34396     
34397     /** 
34398      * Returns the {@link Roo.SplitBar} for this region.
34399      * @return {Roo.SplitBar}
34400      */
34401     getSplitBar : function(){
34402         return this.split;
34403     },
34404     
34405     hide : function(){
34406         this.hideSplitter();
34407         Roo.SplitLayoutRegion.superclass.hide.call(this);
34408     },
34409
34410     hideSplitter : function(){
34411         if(this.split){
34412             this.split.el.setLocation(-2000,-2000);
34413             this.split.el.hide();
34414         }
34415     },
34416
34417     show : function(){
34418         if(this.split){
34419             this.split.el.show();
34420         }
34421         Roo.SplitLayoutRegion.superclass.show.call(this);
34422     },
34423     
34424     beforeSlide: function(){
34425         if(Roo.isGecko){// firefox overflow auto bug workaround
34426             this.bodyEl.clip();
34427             if(this.tabs) this.tabs.bodyEl.clip();
34428             if(this.activePanel){
34429                 this.activePanel.getEl().clip();
34430                 
34431                 if(this.activePanel.beforeSlide){
34432                     this.activePanel.beforeSlide();
34433                 }
34434             }
34435         }
34436     },
34437     
34438     afterSlide : function(){
34439         if(Roo.isGecko){// firefox overflow auto bug workaround
34440             this.bodyEl.unclip();
34441             if(this.tabs) this.tabs.bodyEl.unclip();
34442             if(this.activePanel){
34443                 this.activePanel.getEl().unclip();
34444                 if(this.activePanel.afterSlide){
34445                     this.activePanel.afterSlide();
34446                 }
34447             }
34448         }
34449     },
34450
34451     initAutoHide : function(){
34452         if(this.autoHide !== false){
34453             if(!this.autoHideHd){
34454                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34455                 this.autoHideHd = {
34456                     "mouseout": function(e){
34457                         if(!e.within(this.el, true)){
34458                             st.delay(500);
34459                         }
34460                     },
34461                     "mouseover" : function(e){
34462                         st.cancel();
34463                     },
34464                     scope : this
34465                 };
34466             }
34467             this.el.on(this.autoHideHd);
34468         }
34469     },
34470
34471     clearAutoHide : function(){
34472         if(this.autoHide !== false){
34473             this.el.un("mouseout", this.autoHideHd.mouseout);
34474             this.el.un("mouseover", this.autoHideHd.mouseover);
34475         }
34476     },
34477
34478     clearMonitor : function(){
34479         Roo.get(document).un("click", this.slideInIf, this);
34480     },
34481
34482     // these names are backwards but not changed for compat
34483     slideOut : function(){
34484         if(this.isSlid || this.el.hasActiveFx()){
34485             return;
34486         }
34487         this.isSlid = true;
34488         if(this.collapseBtn){
34489             this.collapseBtn.hide();
34490         }
34491         this.closeBtnState = this.closeBtn.getStyle('display');
34492         this.closeBtn.hide();
34493         if(this.stickBtn){
34494             this.stickBtn.show();
34495         }
34496         this.el.show();
34497         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34498         this.beforeSlide();
34499         this.el.setStyle("z-index", 10001);
34500         this.el.slideIn(this.getSlideAnchor(), {
34501             callback: function(){
34502                 this.afterSlide();
34503                 this.initAutoHide();
34504                 Roo.get(document).on("click", this.slideInIf, this);
34505                 this.fireEvent("slideshow", this);
34506             },
34507             scope: this,
34508             block: true
34509         });
34510     },
34511
34512     afterSlideIn : function(){
34513         this.clearAutoHide();
34514         this.isSlid = false;
34515         this.clearMonitor();
34516         this.el.setStyle("z-index", "");
34517         if(this.collapseBtn){
34518             this.collapseBtn.show();
34519         }
34520         this.closeBtn.setStyle('display', this.closeBtnState);
34521         if(this.stickBtn){
34522             this.stickBtn.hide();
34523         }
34524         this.fireEvent("slidehide", this);
34525     },
34526
34527     slideIn : function(cb){
34528         if(!this.isSlid || this.el.hasActiveFx()){
34529             Roo.callback(cb);
34530             return;
34531         }
34532         this.isSlid = false;
34533         this.beforeSlide();
34534         this.el.slideOut(this.getSlideAnchor(), {
34535             callback: function(){
34536                 this.el.setLeftTop(-10000, -10000);
34537                 this.afterSlide();
34538                 this.afterSlideIn();
34539                 Roo.callback(cb);
34540             },
34541             scope: this,
34542             block: true
34543         });
34544     },
34545     
34546     slideInIf : function(e){
34547         if(!e.within(this.el)){
34548             this.slideIn();
34549         }
34550     },
34551
34552     animateCollapse : function(){
34553         this.beforeSlide();
34554         this.el.setStyle("z-index", 20000);
34555         var anchor = this.getSlideAnchor();
34556         this.el.slideOut(anchor, {
34557             callback : function(){
34558                 this.el.setStyle("z-index", "");
34559                 this.collapsedEl.slideIn(anchor, {duration:.3});
34560                 this.afterSlide();
34561                 this.el.setLocation(-10000,-10000);
34562                 this.el.hide();
34563                 this.fireEvent("collapsed", this);
34564             },
34565             scope: this,
34566             block: true
34567         });
34568     },
34569
34570     animateExpand : function(){
34571         this.beforeSlide();
34572         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34573         this.el.setStyle("z-index", 20000);
34574         this.collapsedEl.hide({
34575             duration:.1
34576         });
34577         this.el.slideIn(this.getSlideAnchor(), {
34578             callback : function(){
34579                 this.el.setStyle("z-index", "");
34580                 this.afterSlide();
34581                 if(this.split){
34582                     this.split.el.show();
34583                 }
34584                 this.fireEvent("invalidated", this);
34585                 this.fireEvent("expanded", this);
34586             },
34587             scope: this,
34588             block: true
34589         });
34590     },
34591
34592     anchors : {
34593         "west" : "left",
34594         "east" : "right",
34595         "north" : "top",
34596         "south" : "bottom"
34597     },
34598
34599     sanchors : {
34600         "west" : "l",
34601         "east" : "r",
34602         "north" : "t",
34603         "south" : "b"
34604     },
34605
34606     canchors : {
34607         "west" : "tl-tr",
34608         "east" : "tr-tl",
34609         "north" : "tl-bl",
34610         "south" : "bl-tl"
34611     },
34612
34613     getAnchor : function(){
34614         return this.anchors[this.position];
34615     },
34616
34617     getCollapseAnchor : function(){
34618         return this.canchors[this.position];
34619     },
34620
34621     getSlideAnchor : function(){
34622         return this.sanchors[this.position];
34623     },
34624
34625     getAlignAdj : function(){
34626         var cm = this.cmargins;
34627         switch(this.position){
34628             case "west":
34629                 return [0, 0];
34630             break;
34631             case "east":
34632                 return [0, 0];
34633             break;
34634             case "north":
34635                 return [0, 0];
34636             break;
34637             case "south":
34638                 return [0, 0];
34639             break;
34640         }
34641     },
34642
34643     getExpandAdj : function(){
34644         var c = this.collapsedEl, cm = this.cmargins;
34645         switch(this.position){
34646             case "west":
34647                 return [-(cm.right+c.getWidth()+cm.left), 0];
34648             break;
34649             case "east":
34650                 return [cm.right+c.getWidth()+cm.left, 0];
34651             break;
34652             case "north":
34653                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34654             break;
34655             case "south":
34656                 return [0, cm.top+cm.bottom+c.getHeight()];
34657             break;
34658         }
34659     }
34660 });/*
34661  * Based on:
34662  * Ext JS Library 1.1.1
34663  * Copyright(c) 2006-2007, Ext JS, LLC.
34664  *
34665  * Originally Released Under LGPL - original licence link has changed is not relivant.
34666  *
34667  * Fork - LGPL
34668  * <script type="text/javascript">
34669  */
34670 /*
34671  * These classes are private internal classes
34672  */
34673 Roo.CenterLayoutRegion = function(mgr, config){
34674     Roo.LayoutRegion.call(this, mgr, config, "center");
34675     this.visible = true;
34676     this.minWidth = config.minWidth || 20;
34677     this.minHeight = config.minHeight || 20;
34678 };
34679
34680 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34681     hide : function(){
34682         // center panel can't be hidden
34683     },
34684     
34685     show : function(){
34686         // center panel can't be hidden
34687     },
34688     
34689     getMinWidth: function(){
34690         return this.minWidth;
34691     },
34692     
34693     getMinHeight: function(){
34694         return this.minHeight;
34695     }
34696 });
34697
34698
34699 Roo.NorthLayoutRegion = function(mgr, config){
34700     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34701     if(this.split){
34702         this.split.placement = Roo.SplitBar.TOP;
34703         this.split.orientation = Roo.SplitBar.VERTICAL;
34704         this.split.el.addClass("x-layout-split-v");
34705     }
34706     var size = config.initialSize || config.height;
34707     if(typeof size != "undefined"){
34708         this.el.setHeight(size);
34709     }
34710 };
34711 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34712     orientation: Roo.SplitBar.VERTICAL,
34713     getBox : function(){
34714         if(this.collapsed){
34715             return this.collapsedEl.getBox();
34716         }
34717         var box = this.el.getBox();
34718         if(this.split){
34719             box.height += this.split.el.getHeight();
34720         }
34721         return box;
34722     },
34723     
34724     updateBox : function(box){
34725         if(this.split && !this.collapsed){
34726             box.height -= this.split.el.getHeight();
34727             this.split.el.setLeft(box.x);
34728             this.split.el.setTop(box.y+box.height);
34729             this.split.el.setWidth(box.width);
34730         }
34731         if(this.collapsed){
34732             this.updateBody(box.width, null);
34733         }
34734         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34735     }
34736 });
34737
34738 Roo.SouthLayoutRegion = function(mgr, config){
34739     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34740     if(this.split){
34741         this.split.placement = Roo.SplitBar.BOTTOM;
34742         this.split.orientation = Roo.SplitBar.VERTICAL;
34743         this.split.el.addClass("x-layout-split-v");
34744     }
34745     var size = config.initialSize || config.height;
34746     if(typeof size != "undefined"){
34747         this.el.setHeight(size);
34748     }
34749 };
34750 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34751     orientation: Roo.SplitBar.VERTICAL,
34752     getBox : function(){
34753         if(this.collapsed){
34754             return this.collapsedEl.getBox();
34755         }
34756         var box = this.el.getBox();
34757         if(this.split){
34758             var sh = this.split.el.getHeight();
34759             box.height += sh;
34760             box.y -= sh;
34761         }
34762         return box;
34763     },
34764     
34765     updateBox : function(box){
34766         if(this.split && !this.collapsed){
34767             var sh = this.split.el.getHeight();
34768             box.height -= sh;
34769             box.y += sh;
34770             this.split.el.setLeft(box.x);
34771             this.split.el.setTop(box.y-sh);
34772             this.split.el.setWidth(box.width);
34773         }
34774         if(this.collapsed){
34775             this.updateBody(box.width, null);
34776         }
34777         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34778     }
34779 });
34780
34781 Roo.EastLayoutRegion = function(mgr, config){
34782     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34783     if(this.split){
34784         this.split.placement = Roo.SplitBar.RIGHT;
34785         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34786         this.split.el.addClass("x-layout-split-h");
34787     }
34788     var size = config.initialSize || config.width;
34789     if(typeof size != "undefined"){
34790         this.el.setWidth(size);
34791     }
34792 };
34793 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34794     orientation: Roo.SplitBar.HORIZONTAL,
34795     getBox : function(){
34796         if(this.collapsed){
34797             return this.collapsedEl.getBox();
34798         }
34799         var box = this.el.getBox();
34800         if(this.split){
34801             var sw = this.split.el.getWidth();
34802             box.width += sw;
34803             box.x -= sw;
34804         }
34805         return box;
34806     },
34807
34808     updateBox : function(box){
34809         if(this.split && !this.collapsed){
34810             var sw = this.split.el.getWidth();
34811             box.width -= sw;
34812             this.split.el.setLeft(box.x);
34813             this.split.el.setTop(box.y);
34814             this.split.el.setHeight(box.height);
34815             box.x += sw;
34816         }
34817         if(this.collapsed){
34818             this.updateBody(null, box.height);
34819         }
34820         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34821     }
34822 });
34823
34824 Roo.WestLayoutRegion = function(mgr, config){
34825     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34826     if(this.split){
34827         this.split.placement = Roo.SplitBar.LEFT;
34828         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34829         this.split.el.addClass("x-layout-split-h");
34830     }
34831     var size = config.initialSize || config.width;
34832     if(typeof size != "undefined"){
34833         this.el.setWidth(size);
34834     }
34835 };
34836 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34837     orientation: Roo.SplitBar.HORIZONTAL,
34838     getBox : function(){
34839         if(this.collapsed){
34840             return this.collapsedEl.getBox();
34841         }
34842         var box = this.el.getBox();
34843         if(this.split){
34844             box.width += this.split.el.getWidth();
34845         }
34846         return box;
34847     },
34848     
34849     updateBox : function(box){
34850         if(this.split && !this.collapsed){
34851             var sw = this.split.el.getWidth();
34852             box.width -= sw;
34853             this.split.el.setLeft(box.x+box.width);
34854             this.split.el.setTop(box.y);
34855             this.split.el.setHeight(box.height);
34856         }
34857         if(this.collapsed){
34858             this.updateBody(null, box.height);
34859         }
34860         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34861     }
34862 });
34863 /*
34864  * Based on:
34865  * Ext JS Library 1.1.1
34866  * Copyright(c) 2006-2007, Ext JS, LLC.
34867  *
34868  * Originally Released Under LGPL - original licence link has changed is not relivant.
34869  *
34870  * Fork - LGPL
34871  * <script type="text/javascript">
34872  */
34873  
34874  
34875 /*
34876  * Private internal class for reading and applying state
34877  */
34878 Roo.LayoutStateManager = function(layout){
34879      // default empty state
34880      this.state = {
34881         north: {},
34882         south: {},
34883         east: {},
34884         west: {}       
34885     };
34886 };
34887
34888 Roo.LayoutStateManager.prototype = {
34889     init : function(layout, provider){
34890         this.provider = provider;
34891         var state = provider.get(layout.id+"-layout-state");
34892         if(state){
34893             var wasUpdating = layout.isUpdating();
34894             if(!wasUpdating){
34895                 layout.beginUpdate();
34896             }
34897             for(var key in state){
34898                 if(typeof state[key] != "function"){
34899                     var rstate = state[key];
34900                     var r = layout.getRegion(key);
34901                     if(r && rstate){
34902                         if(rstate.size){
34903                             r.resizeTo(rstate.size);
34904                         }
34905                         if(rstate.collapsed == true){
34906                             r.collapse(true);
34907                         }else{
34908                             r.expand(null, true);
34909                         }
34910                     }
34911                 }
34912             }
34913             if(!wasUpdating){
34914                 layout.endUpdate();
34915             }
34916             this.state = state; 
34917         }
34918         this.layout = layout;
34919         layout.on("regionresized", this.onRegionResized, this);
34920         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34921         layout.on("regionexpanded", this.onRegionExpanded, this);
34922     },
34923     
34924     storeState : function(){
34925         this.provider.set(this.layout.id+"-layout-state", this.state);
34926     },
34927     
34928     onRegionResized : function(region, newSize){
34929         this.state[region.getPosition()].size = newSize;
34930         this.storeState();
34931     },
34932     
34933     onRegionCollapsed : function(region){
34934         this.state[region.getPosition()].collapsed = true;
34935         this.storeState();
34936     },
34937     
34938     onRegionExpanded : function(region){
34939         this.state[region.getPosition()].collapsed = false;
34940         this.storeState();
34941     }
34942 };/*
34943  * Based on:
34944  * Ext JS Library 1.1.1
34945  * Copyright(c) 2006-2007, Ext JS, LLC.
34946  *
34947  * Originally Released Under LGPL - original licence link has changed is not relivant.
34948  *
34949  * Fork - LGPL
34950  * <script type="text/javascript">
34951  */
34952 /**
34953  * @class Roo.ContentPanel
34954  * @extends Roo.util.Observable
34955  * A basic ContentPanel element.
34956  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34957  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34958  * @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
34959  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34960  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34961  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34962  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34963  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34964  * @cfg {String} title          The title for this panel
34965  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34966  * @cfg {String} url            Calls {@link #setUrl} with this value
34967  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34968  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34969  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34970  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34971
34972  * @constructor
34973  * Create a new ContentPanel.
34974  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34975  * @param {String/Object} config A string to set only the title or a config object
34976  * @param {String} content (optional) Set the HTML content for this panel
34977  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34978  */
34979 Roo.ContentPanel = function(el, config, content){
34980     
34981      
34982     /*
34983     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34984         config = el;
34985         el = Roo.id();
34986     }
34987     if (config && config.parentLayout) { 
34988         el = config.parentLayout.el.createChild(); 
34989     }
34990     */
34991     if(el.autoCreate){ // xtype is available if this is called from factory
34992         config = el;
34993         el = Roo.id();
34994     }
34995     this.el = Roo.get(el);
34996     if(!this.el && config && config.autoCreate){
34997         if(typeof config.autoCreate == "object"){
34998             if(!config.autoCreate.id){
34999                 config.autoCreate.id = config.id||el;
35000             }
35001             this.el = Roo.DomHelper.append(document.body,
35002                         config.autoCreate, true);
35003         }else{
35004             this.el = Roo.DomHelper.append(document.body,
35005                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35006         }
35007     }
35008     this.closable = false;
35009     this.loaded = false;
35010     this.active = false;
35011     if(typeof config == "string"){
35012         this.title = config;
35013     }else{
35014         Roo.apply(this, config);
35015     }
35016     
35017     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35018         this.wrapEl = this.el.wrap();
35019         this.toolbar.container = this.el.insertSibling(false, 'before');
35020         this.toolbar = new Roo.Toolbar(this.toolbar);
35021     }
35022     
35023     // xtype created footer. - not sure if will work as we normally have to render first..
35024     if (this.footer && !this.footer.el && this.footer.xtype) {
35025         if (!this.wrapEl) {
35026             this.wrapEl = this.el.wrap();
35027         }
35028     
35029         this.footer.container = this.wrapEl.createChild();
35030          
35031         this.footer = Roo.factory(this.footer, Roo);
35032         
35033     }
35034     
35035     if(this.resizeEl){
35036         this.resizeEl = Roo.get(this.resizeEl, true);
35037     }else{
35038         this.resizeEl = this.el;
35039     }
35040     // handle view.xtype
35041     
35042  
35043     
35044     
35045     this.addEvents({
35046         /**
35047          * @event activate
35048          * Fires when this panel is activated. 
35049          * @param {Roo.ContentPanel} this
35050          */
35051         "activate" : true,
35052         /**
35053          * @event deactivate
35054          * Fires when this panel is activated. 
35055          * @param {Roo.ContentPanel} this
35056          */
35057         "deactivate" : true,
35058
35059         /**
35060          * @event resize
35061          * Fires when this panel is resized if fitToFrame is true.
35062          * @param {Roo.ContentPanel} this
35063          * @param {Number} width The width after any component adjustments
35064          * @param {Number} height The height after any component adjustments
35065          */
35066         "resize" : true,
35067         
35068          /**
35069          * @event render
35070          * Fires when this tab is created
35071          * @param {Roo.ContentPanel} this
35072          */
35073         "render" : true
35074         
35075         
35076         
35077     });
35078     
35079
35080     
35081     
35082     if(this.autoScroll){
35083         this.resizeEl.setStyle("overflow", "auto");
35084     } else {
35085         // fix randome scrolling
35086         this.el.on('scroll', function() {
35087             Roo.log('fix random scolling');
35088             this.scrollTo('top',0); 
35089         });
35090     }
35091     content = content || this.content;
35092     if(content){
35093         this.setContent(content);
35094     }
35095     if(config && config.url){
35096         this.setUrl(this.url, this.params, this.loadOnce);
35097     }
35098     
35099     
35100     
35101     Roo.ContentPanel.superclass.constructor.call(this);
35102     
35103     if (this.view && typeof(this.view.xtype) != 'undefined') {
35104         this.view.el = this.el.appendChild(document.createElement("div"));
35105         this.view = Roo.factory(this.view); 
35106         this.view.render  &&  this.view.render(false, '');  
35107     }
35108     
35109     
35110     this.fireEvent('render', this);
35111 };
35112
35113 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35114     tabTip:'',
35115     setRegion : function(region){
35116         this.region = region;
35117         if(region){
35118            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35119         }else{
35120            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35121         } 
35122     },
35123     
35124     /**
35125      * Returns the toolbar for this Panel if one was configured. 
35126      * @return {Roo.Toolbar} 
35127      */
35128     getToolbar : function(){
35129         return this.toolbar;
35130     },
35131     
35132     setActiveState : function(active){
35133         this.active = active;
35134         if(!active){
35135             this.fireEvent("deactivate", this);
35136         }else{
35137             this.fireEvent("activate", this);
35138         }
35139     },
35140     /**
35141      * Updates this panel's element
35142      * @param {String} content The new content
35143      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35144     */
35145     setContent : function(content, loadScripts){
35146         this.el.update(content, loadScripts);
35147     },
35148
35149     ignoreResize : function(w, h){
35150         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35151             return true;
35152         }else{
35153             this.lastSize = {width: w, height: h};
35154             return false;
35155         }
35156     },
35157     /**
35158      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35159      * @return {Roo.UpdateManager} The UpdateManager
35160      */
35161     getUpdateManager : function(){
35162         return this.el.getUpdateManager();
35163     },
35164      /**
35165      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35166      * @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:
35167 <pre><code>
35168 panel.load({
35169     url: "your-url.php",
35170     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35171     callback: yourFunction,
35172     scope: yourObject, //(optional scope)
35173     discardUrl: false,
35174     nocache: false,
35175     text: "Loading...",
35176     timeout: 30,
35177     scripts: false
35178 });
35179 </code></pre>
35180      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35181      * 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.
35182      * @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}
35183      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35184      * @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.
35185      * @return {Roo.ContentPanel} this
35186      */
35187     load : function(){
35188         var um = this.el.getUpdateManager();
35189         um.update.apply(um, arguments);
35190         return this;
35191     },
35192
35193
35194     /**
35195      * 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.
35196      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35197      * @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)
35198      * @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)
35199      * @return {Roo.UpdateManager} The UpdateManager
35200      */
35201     setUrl : function(url, params, loadOnce){
35202         if(this.refreshDelegate){
35203             this.removeListener("activate", this.refreshDelegate);
35204         }
35205         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35206         this.on("activate", this.refreshDelegate);
35207         return this.el.getUpdateManager();
35208     },
35209     
35210     _handleRefresh : function(url, params, loadOnce){
35211         if(!loadOnce || !this.loaded){
35212             var updater = this.el.getUpdateManager();
35213             updater.update(url, params, this._setLoaded.createDelegate(this));
35214         }
35215     },
35216     
35217     _setLoaded : function(){
35218         this.loaded = true;
35219     }, 
35220     
35221     /**
35222      * Returns this panel's id
35223      * @return {String} 
35224      */
35225     getId : function(){
35226         return this.el.id;
35227     },
35228     
35229     /** 
35230      * Returns this panel's element - used by regiosn to add.
35231      * @return {Roo.Element} 
35232      */
35233     getEl : function(){
35234         return this.wrapEl || this.el;
35235     },
35236     
35237     adjustForComponents : function(width, height)
35238     {
35239         //Roo.log('adjustForComponents ');
35240         if(this.resizeEl != this.el){
35241             width -= this.el.getFrameWidth('lr');
35242             height -= this.el.getFrameWidth('tb');
35243         }
35244         if(this.toolbar){
35245             var te = this.toolbar.getEl();
35246             height -= te.getHeight();
35247             te.setWidth(width);
35248         }
35249         if(this.footer){
35250             var te = this.footer.getEl();
35251             Roo.log("footer:" + te.getHeight());
35252             
35253             height -= te.getHeight();
35254             te.setWidth(width);
35255         }
35256         
35257         
35258         if(this.adjustments){
35259             width += this.adjustments[0];
35260             height += this.adjustments[1];
35261         }
35262         return {"width": width, "height": height};
35263     },
35264     
35265     setSize : function(width, height){
35266         if(this.fitToFrame && !this.ignoreResize(width, height)){
35267             if(this.fitContainer && this.resizeEl != this.el){
35268                 this.el.setSize(width, height);
35269             }
35270             var size = this.adjustForComponents(width, height);
35271             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35272             this.fireEvent('resize', this, size.width, size.height);
35273         }
35274     },
35275     
35276     /**
35277      * Returns this panel's title
35278      * @return {String} 
35279      */
35280     getTitle : function(){
35281         return this.title;
35282     },
35283     
35284     /**
35285      * Set this panel's title
35286      * @param {String} title
35287      */
35288     setTitle : function(title){
35289         this.title = title;
35290         if(this.region){
35291             this.region.updatePanelTitle(this, title);
35292         }
35293     },
35294     
35295     /**
35296      * Returns true is this panel was configured to be closable
35297      * @return {Boolean} 
35298      */
35299     isClosable : function(){
35300         return this.closable;
35301     },
35302     
35303     beforeSlide : function(){
35304         this.el.clip();
35305         this.resizeEl.clip();
35306     },
35307     
35308     afterSlide : function(){
35309         this.el.unclip();
35310         this.resizeEl.unclip();
35311     },
35312     
35313     /**
35314      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35315      *   Will fail silently if the {@link #setUrl} method has not been called.
35316      *   This does not activate the panel, just updates its content.
35317      */
35318     refresh : function(){
35319         if(this.refreshDelegate){
35320            this.loaded = false;
35321            this.refreshDelegate();
35322         }
35323     },
35324     
35325     /**
35326      * Destroys this panel
35327      */
35328     destroy : function(){
35329         this.el.removeAllListeners();
35330         var tempEl = document.createElement("span");
35331         tempEl.appendChild(this.el.dom);
35332         tempEl.innerHTML = "";
35333         this.el.remove();
35334         this.el = null;
35335     },
35336     
35337     /**
35338      * form - if the content panel contains a form - this is a reference to it.
35339      * @type {Roo.form.Form}
35340      */
35341     form : false,
35342     /**
35343      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35344      *    This contains a reference to it.
35345      * @type {Roo.View}
35346      */
35347     view : false,
35348     
35349       /**
35350      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35351      * <pre><code>
35352
35353 layout.addxtype({
35354        xtype : 'Form',
35355        items: [ .... ]
35356    }
35357 );
35358
35359 </code></pre>
35360      * @param {Object} cfg Xtype definition of item to add.
35361      */
35362     
35363     addxtype : function(cfg) {
35364         // add form..
35365         if (cfg.xtype.match(/^Form$/)) {
35366             
35367             var el;
35368             //if (this.footer) {
35369             //    el = this.footer.container.insertSibling(false, 'before');
35370             //} else {
35371                 el = this.el.createChild();
35372             //}
35373
35374             this.form = new  Roo.form.Form(cfg);
35375             
35376             
35377             if ( this.form.allItems.length) this.form.render(el.dom);
35378             return this.form;
35379         }
35380         // should only have one of theses..
35381         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35382             // views.. should not be just added - used named prop 'view''
35383             
35384             cfg.el = this.el.appendChild(document.createElement("div"));
35385             // factory?
35386             
35387             var ret = new Roo.factory(cfg);
35388              
35389              ret.render && ret.render(false, ''); // render blank..
35390             this.view = ret;
35391             return ret;
35392         }
35393         return false;
35394     }
35395 });
35396
35397 /**
35398  * @class Roo.GridPanel
35399  * @extends Roo.ContentPanel
35400  * @constructor
35401  * Create a new GridPanel.
35402  * @param {Roo.grid.Grid} grid The grid for this panel
35403  * @param {String/Object} config A string to set only the panel's title, or a config object
35404  */
35405 Roo.GridPanel = function(grid, config){
35406     
35407   
35408     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35409         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35410         
35411     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35412     
35413     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35414     
35415     if(this.toolbar){
35416         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35417     }
35418     // xtype created footer. - not sure if will work as we normally have to render first..
35419     if (this.footer && !this.footer.el && this.footer.xtype) {
35420         
35421         this.footer.container = this.grid.getView().getFooterPanel(true);
35422         this.footer.dataSource = this.grid.dataSource;
35423         this.footer = Roo.factory(this.footer, Roo);
35424         
35425     }
35426     
35427     grid.monitorWindowResize = false; // turn off autosizing
35428     grid.autoHeight = false;
35429     grid.autoWidth = false;
35430     this.grid = grid;
35431     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35432 };
35433
35434 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35435     getId : function(){
35436         return this.grid.id;
35437     },
35438     
35439     /**
35440      * Returns the grid for this panel
35441      * @return {Roo.grid.Grid} 
35442      */
35443     getGrid : function(){
35444         return this.grid;    
35445     },
35446     
35447     setSize : function(width, height){
35448         if(!this.ignoreResize(width, height)){
35449             var grid = this.grid;
35450             var size = this.adjustForComponents(width, height);
35451             grid.getGridEl().setSize(size.width, size.height);
35452             grid.autoSize();
35453         }
35454     },
35455     
35456     beforeSlide : function(){
35457         this.grid.getView().scroller.clip();
35458     },
35459     
35460     afterSlide : function(){
35461         this.grid.getView().scroller.unclip();
35462     },
35463     
35464     destroy : function(){
35465         this.grid.destroy();
35466         delete this.grid;
35467         Roo.GridPanel.superclass.destroy.call(this); 
35468     }
35469 });
35470
35471
35472 /**
35473  * @class Roo.NestedLayoutPanel
35474  * @extends Roo.ContentPanel
35475  * @constructor
35476  * Create a new NestedLayoutPanel.
35477  * 
35478  * 
35479  * @param {Roo.BorderLayout} layout The layout for this panel
35480  * @param {String/Object} config A string to set only the title or a config object
35481  */
35482 Roo.NestedLayoutPanel = function(layout, config)
35483 {
35484     // construct with only one argument..
35485     /* FIXME - implement nicer consturctors
35486     if (layout.layout) {
35487         config = layout;
35488         layout = config.layout;
35489         delete config.layout;
35490     }
35491     if (layout.xtype && !layout.getEl) {
35492         // then layout needs constructing..
35493         layout = Roo.factory(layout, Roo);
35494     }
35495     */
35496     
35497     
35498     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35499     
35500     layout.monitorWindowResize = false; // turn off autosizing
35501     this.layout = layout;
35502     this.layout.getEl().addClass("x-layout-nested-layout");
35503     
35504     
35505     
35506     
35507 };
35508
35509 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35510
35511     setSize : function(width, height){
35512         if(!this.ignoreResize(width, height)){
35513             var size = this.adjustForComponents(width, height);
35514             var el = this.layout.getEl();
35515             el.setSize(size.width, size.height);
35516             var touch = el.dom.offsetWidth;
35517             this.layout.layout();
35518             // ie requires a double layout on the first pass
35519             if(Roo.isIE && !this.initialized){
35520                 this.initialized = true;
35521                 this.layout.layout();
35522             }
35523         }
35524     },
35525     
35526     // activate all subpanels if not currently active..
35527     
35528     setActiveState : function(active){
35529         this.active = active;
35530         if(!active){
35531             this.fireEvent("deactivate", this);
35532             return;
35533         }
35534         
35535         this.fireEvent("activate", this);
35536         // not sure if this should happen before or after..
35537         if (!this.layout) {
35538             return; // should not happen..
35539         }
35540         var reg = false;
35541         for (var r in this.layout.regions) {
35542             reg = this.layout.getRegion(r);
35543             if (reg.getActivePanel()) {
35544                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35545                 reg.setActivePanel(reg.getActivePanel());
35546                 continue;
35547             }
35548             if (!reg.panels.length) {
35549                 continue;
35550             }
35551             reg.showPanel(reg.getPanel(0));
35552         }
35553         
35554         
35555         
35556         
35557     },
35558     
35559     /**
35560      * Returns the nested BorderLayout for this panel
35561      * @return {Roo.BorderLayout} 
35562      */
35563     getLayout : function(){
35564         return this.layout;
35565     },
35566     
35567      /**
35568      * Adds a xtype elements to the layout of the nested panel
35569      * <pre><code>
35570
35571 panel.addxtype({
35572        xtype : 'ContentPanel',
35573        region: 'west',
35574        items: [ .... ]
35575    }
35576 );
35577
35578 panel.addxtype({
35579         xtype : 'NestedLayoutPanel',
35580         region: 'west',
35581         layout: {
35582            center: { },
35583            west: { }   
35584         },
35585         items : [ ... list of content panels or nested layout panels.. ]
35586    }
35587 );
35588 </code></pre>
35589      * @param {Object} cfg Xtype definition of item to add.
35590      */
35591     addxtype : function(cfg) {
35592         return this.layout.addxtype(cfg);
35593     
35594     }
35595 });
35596
35597 Roo.ScrollPanel = function(el, config, content){
35598     config = config || {};
35599     config.fitToFrame = true;
35600     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35601     
35602     this.el.dom.style.overflow = "hidden";
35603     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35604     this.el.removeClass("x-layout-inactive-content");
35605     this.el.on("mousewheel", this.onWheel, this);
35606
35607     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35608     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35609     up.unselectable(); down.unselectable();
35610     up.on("click", this.scrollUp, this);
35611     down.on("click", this.scrollDown, this);
35612     up.addClassOnOver("x-scroller-btn-over");
35613     down.addClassOnOver("x-scroller-btn-over");
35614     up.addClassOnClick("x-scroller-btn-click");
35615     down.addClassOnClick("x-scroller-btn-click");
35616     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35617
35618     this.resizeEl = this.el;
35619     this.el = wrap; this.up = up; this.down = down;
35620 };
35621
35622 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35623     increment : 100,
35624     wheelIncrement : 5,
35625     scrollUp : function(){
35626         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35627     },
35628
35629     scrollDown : function(){
35630         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35631     },
35632
35633     afterScroll : function(){
35634         var el = this.resizeEl;
35635         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35636         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35637         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35638     },
35639
35640     setSize : function(){
35641         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35642         this.afterScroll();
35643     },
35644
35645     onWheel : function(e){
35646         var d = e.getWheelDelta();
35647         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35648         this.afterScroll();
35649         e.stopEvent();
35650     },
35651
35652     setContent : function(content, loadScripts){
35653         this.resizeEl.update(content, loadScripts);
35654     }
35655
35656 });
35657
35658
35659
35660
35661
35662
35663
35664
35665
35666 /**
35667  * @class Roo.TreePanel
35668  * @extends Roo.ContentPanel
35669  * @constructor
35670  * Create a new TreePanel. - defaults to fit/scoll contents.
35671  * @param {String/Object} config A string to set only the panel's title, or a config object
35672  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35673  */
35674 Roo.TreePanel = function(config){
35675     var el = config.el;
35676     var tree = config.tree;
35677     delete config.tree; 
35678     delete config.el; // hopefull!
35679     
35680     // wrapper for IE7 strict & safari scroll issue
35681     
35682     var treeEl = el.createChild();
35683     config.resizeEl = treeEl;
35684     
35685     
35686     
35687     Roo.TreePanel.superclass.constructor.call(this, el, config);
35688  
35689  
35690     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35691     //console.log(tree);
35692     this.on('activate', function()
35693     {
35694         if (this.tree.rendered) {
35695             return;
35696         }
35697         //console.log('render tree');
35698         this.tree.render();
35699     });
35700     // this should not be needed.. - it's actually the 'el' that resizes?
35701     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35702     
35703     //this.on('resize',  function (cp, w, h) {
35704     //        this.tree.innerCt.setWidth(w);
35705     //        this.tree.innerCt.setHeight(h);
35706     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35707     //});
35708
35709         
35710     
35711 };
35712
35713 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35714     fitToFrame : true,
35715     autoScroll : true
35716 });
35717
35718
35719
35720
35721
35722
35723
35724
35725
35726
35727
35728 /*
35729  * Based on:
35730  * Ext JS Library 1.1.1
35731  * Copyright(c) 2006-2007, Ext JS, LLC.
35732  *
35733  * Originally Released Under LGPL - original licence link has changed is not relivant.
35734  *
35735  * Fork - LGPL
35736  * <script type="text/javascript">
35737  */
35738  
35739
35740 /**
35741  * @class Roo.ReaderLayout
35742  * @extends Roo.BorderLayout
35743  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35744  * center region containing two nested regions (a top one for a list view and one for item preview below),
35745  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35746  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35747  * expedites the setup of the overall layout and regions for this common application style.
35748  * Example:
35749  <pre><code>
35750 var reader = new Roo.ReaderLayout();
35751 var CP = Roo.ContentPanel;  // shortcut for adding
35752
35753 reader.beginUpdate();
35754 reader.add("north", new CP("north", "North"));
35755 reader.add("west", new CP("west", {title: "West"}));
35756 reader.add("east", new CP("east", {title: "East"}));
35757
35758 reader.regions.listView.add(new CP("listView", "List"));
35759 reader.regions.preview.add(new CP("preview", "Preview"));
35760 reader.endUpdate();
35761 </code></pre>
35762 * @constructor
35763 * Create a new ReaderLayout
35764 * @param {Object} config Configuration options
35765 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35766 * document.body if omitted)
35767 */
35768 Roo.ReaderLayout = function(config, renderTo){
35769     var c = config || {size:{}};
35770     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35771         north: c.north !== false ? Roo.apply({
35772             split:false,
35773             initialSize: 32,
35774             titlebar: false
35775         }, c.north) : false,
35776         west: c.west !== false ? Roo.apply({
35777             split:true,
35778             initialSize: 200,
35779             minSize: 175,
35780             maxSize: 400,
35781             titlebar: true,
35782             collapsible: true,
35783             animate: true,
35784             margins:{left:5,right:0,bottom:5,top:5},
35785             cmargins:{left:5,right:5,bottom:5,top:5}
35786         }, c.west) : false,
35787         east: c.east !== false ? Roo.apply({
35788             split:true,
35789             initialSize: 200,
35790             minSize: 175,
35791             maxSize: 400,
35792             titlebar: true,
35793             collapsible: true,
35794             animate: true,
35795             margins:{left:0,right:5,bottom:5,top:5},
35796             cmargins:{left:5,right:5,bottom:5,top:5}
35797         }, c.east) : false,
35798         center: Roo.apply({
35799             tabPosition: 'top',
35800             autoScroll:false,
35801             closeOnTab: true,
35802             titlebar:false,
35803             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35804         }, c.center)
35805     });
35806
35807     this.el.addClass('x-reader');
35808
35809     this.beginUpdate();
35810
35811     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35812         south: c.preview !== false ? Roo.apply({
35813             split:true,
35814             initialSize: 200,
35815             minSize: 100,
35816             autoScroll:true,
35817             collapsible:true,
35818             titlebar: true,
35819             cmargins:{top:5,left:0, right:0, bottom:0}
35820         }, c.preview) : false,
35821         center: Roo.apply({
35822             autoScroll:false,
35823             titlebar:false,
35824             minHeight:200
35825         }, c.listView)
35826     });
35827     this.add('center', new Roo.NestedLayoutPanel(inner,
35828             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35829
35830     this.endUpdate();
35831
35832     this.regions.preview = inner.getRegion('south');
35833     this.regions.listView = inner.getRegion('center');
35834 };
35835
35836 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35837  * Based on:
35838  * Ext JS Library 1.1.1
35839  * Copyright(c) 2006-2007, Ext JS, LLC.
35840  *
35841  * Originally Released Under LGPL - original licence link has changed is not relivant.
35842  *
35843  * Fork - LGPL
35844  * <script type="text/javascript">
35845  */
35846  
35847 /**
35848  * @class Roo.grid.Grid
35849  * @extends Roo.util.Observable
35850  * This class represents the primary interface of a component based grid control.
35851  * <br><br>Usage:<pre><code>
35852  var grid = new Roo.grid.Grid("my-container-id", {
35853      ds: myDataStore,
35854      cm: myColModel,
35855      selModel: mySelectionModel,
35856      autoSizeColumns: true,
35857      monitorWindowResize: false,
35858      trackMouseOver: true
35859  });
35860  // set any options
35861  grid.render();
35862  * </code></pre>
35863  * <b>Common Problems:</b><br/>
35864  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35865  * element will correct this<br/>
35866  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35867  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35868  * are unpredictable.<br/>
35869  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35870  * grid to calculate dimensions/offsets.<br/>
35871   * @constructor
35872  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35873  * The container MUST have some type of size defined for the grid to fill. The container will be
35874  * automatically set to position relative if it isn't already.
35875  * @param {Object} config A config object that sets properties on this grid.
35876  */
35877 Roo.grid.Grid = function(container, config){
35878         // initialize the container
35879         this.container = Roo.get(container);
35880         this.container.update("");
35881         this.container.setStyle("overflow", "hidden");
35882     this.container.addClass('x-grid-container');
35883
35884     this.id = this.container.id;
35885
35886     Roo.apply(this, config);
35887     // check and correct shorthanded configs
35888     if(this.ds){
35889         this.dataSource = this.ds;
35890         delete this.ds;
35891     }
35892     if(this.cm){
35893         this.colModel = this.cm;
35894         delete this.cm;
35895     }
35896     if(this.sm){
35897         this.selModel = this.sm;
35898         delete this.sm;
35899     }
35900
35901     if (this.selModel) {
35902         this.selModel = Roo.factory(this.selModel, Roo.grid);
35903         this.sm = this.selModel;
35904         this.sm.xmodule = this.xmodule || false;
35905     }
35906     if (typeof(this.colModel.config) == 'undefined') {
35907         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35908         this.cm = this.colModel;
35909         this.cm.xmodule = this.xmodule || false;
35910     }
35911     if (this.dataSource) {
35912         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35913         this.ds = this.dataSource;
35914         this.ds.xmodule = this.xmodule || false;
35915          
35916     }
35917     
35918     
35919     
35920     if(this.width){
35921         this.container.setWidth(this.width);
35922     }
35923
35924     if(this.height){
35925         this.container.setHeight(this.height);
35926     }
35927     /** @private */
35928         this.addEvents({
35929         // raw events
35930         /**
35931          * @event click
35932          * The raw click event for the entire grid.
35933          * @param {Roo.EventObject} e
35934          */
35935         "click" : true,
35936         /**
35937          * @event dblclick
35938          * The raw dblclick event for the entire grid.
35939          * @param {Roo.EventObject} e
35940          */
35941         "dblclick" : true,
35942         /**
35943          * @event contextmenu
35944          * The raw contextmenu event for the entire grid.
35945          * @param {Roo.EventObject} e
35946          */
35947         "contextmenu" : true,
35948         /**
35949          * @event mousedown
35950          * The raw mousedown event for the entire grid.
35951          * @param {Roo.EventObject} e
35952          */
35953         "mousedown" : true,
35954         /**
35955          * @event mouseup
35956          * The raw mouseup event for the entire grid.
35957          * @param {Roo.EventObject} e
35958          */
35959         "mouseup" : true,
35960         /**
35961          * @event mouseover
35962          * The raw mouseover event for the entire grid.
35963          * @param {Roo.EventObject} e
35964          */
35965         "mouseover" : true,
35966         /**
35967          * @event mouseout
35968          * The raw mouseout event for the entire grid.
35969          * @param {Roo.EventObject} e
35970          */
35971         "mouseout" : true,
35972         /**
35973          * @event keypress
35974          * The raw keypress event for the entire grid.
35975          * @param {Roo.EventObject} e
35976          */
35977         "keypress" : true,
35978         /**
35979          * @event keydown
35980          * The raw keydown event for the entire grid.
35981          * @param {Roo.EventObject} e
35982          */
35983         "keydown" : true,
35984
35985         // custom events
35986
35987         /**
35988          * @event cellclick
35989          * Fires when a cell is clicked
35990          * @param {Grid} this
35991          * @param {Number} rowIndex
35992          * @param {Number} columnIndex
35993          * @param {Roo.EventObject} e
35994          */
35995         "cellclick" : true,
35996         /**
35997          * @event celldblclick
35998          * Fires when a cell is double clicked
35999          * @param {Grid} this
36000          * @param {Number} rowIndex
36001          * @param {Number} columnIndex
36002          * @param {Roo.EventObject} e
36003          */
36004         "celldblclick" : true,
36005         /**
36006          * @event rowclick
36007          * Fires when a row is clicked
36008          * @param {Grid} this
36009          * @param {Number} rowIndex
36010          * @param {Roo.EventObject} e
36011          */
36012         "rowclick" : true,
36013         /**
36014          * @event rowdblclick
36015          * Fires when a row is double clicked
36016          * @param {Grid} this
36017          * @param {Number} rowIndex
36018          * @param {Roo.EventObject} e
36019          */
36020         "rowdblclick" : true,
36021         /**
36022          * @event headerclick
36023          * Fires when a header is clicked
36024          * @param {Grid} this
36025          * @param {Number} columnIndex
36026          * @param {Roo.EventObject} e
36027          */
36028         "headerclick" : true,
36029         /**
36030          * @event headerdblclick
36031          * Fires when a header cell is double clicked
36032          * @param {Grid} this
36033          * @param {Number} columnIndex
36034          * @param {Roo.EventObject} e
36035          */
36036         "headerdblclick" : true,
36037         /**
36038          * @event rowcontextmenu
36039          * Fires when a row is right clicked
36040          * @param {Grid} this
36041          * @param {Number} rowIndex
36042          * @param {Roo.EventObject} e
36043          */
36044         "rowcontextmenu" : true,
36045         /**
36046          * @event cellcontextmenu
36047          * Fires when a cell is right clicked
36048          * @param {Grid} this
36049          * @param {Number} rowIndex
36050          * @param {Number} cellIndex
36051          * @param {Roo.EventObject} e
36052          */
36053          "cellcontextmenu" : true,
36054         /**
36055          * @event headercontextmenu
36056          * Fires when a header is right clicked
36057          * @param {Grid} this
36058          * @param {Number} columnIndex
36059          * @param {Roo.EventObject} e
36060          */
36061         "headercontextmenu" : true,
36062         /**
36063          * @event bodyscroll
36064          * Fires when the body element is scrolled
36065          * @param {Number} scrollLeft
36066          * @param {Number} scrollTop
36067          */
36068         "bodyscroll" : true,
36069         /**
36070          * @event columnresize
36071          * Fires when the user resizes a column
36072          * @param {Number} columnIndex
36073          * @param {Number} newSize
36074          */
36075         "columnresize" : true,
36076         /**
36077          * @event columnmove
36078          * Fires when the user moves a column
36079          * @param {Number} oldIndex
36080          * @param {Number} newIndex
36081          */
36082         "columnmove" : true,
36083         /**
36084          * @event startdrag
36085          * Fires when row(s) start being dragged
36086          * @param {Grid} this
36087          * @param {Roo.GridDD} dd The drag drop object
36088          * @param {event} e The raw browser event
36089          */
36090         "startdrag" : true,
36091         /**
36092          * @event enddrag
36093          * Fires when a drag operation is complete
36094          * @param {Grid} this
36095          * @param {Roo.GridDD} dd The drag drop object
36096          * @param {event} e The raw browser event
36097          */
36098         "enddrag" : true,
36099         /**
36100          * @event dragdrop
36101          * Fires when dragged row(s) are dropped on a valid DD target
36102          * @param {Grid} this
36103          * @param {Roo.GridDD} dd The drag drop object
36104          * @param {String} targetId The target drag drop object
36105          * @param {event} e The raw browser event
36106          */
36107         "dragdrop" : true,
36108         /**
36109          * @event dragover
36110          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36111          * @param {Grid} this
36112          * @param {Roo.GridDD} dd The drag drop object
36113          * @param {String} targetId The target drag drop object
36114          * @param {event} e The raw browser event
36115          */
36116         "dragover" : true,
36117         /**
36118          * @event dragenter
36119          *  Fires when the dragged row(s) first cross another DD target while being dragged
36120          * @param {Grid} this
36121          * @param {Roo.GridDD} dd The drag drop object
36122          * @param {String} targetId The target drag drop object
36123          * @param {event} e The raw browser event
36124          */
36125         "dragenter" : true,
36126         /**
36127          * @event dragout
36128          * Fires when the dragged row(s) leave another DD target while being dragged
36129          * @param {Grid} this
36130          * @param {Roo.GridDD} dd The drag drop object
36131          * @param {String} targetId The target drag drop object
36132          * @param {event} e The raw browser event
36133          */
36134         "dragout" : true,
36135         /**
36136          * @event rowclass
36137          * Fires when a row is rendered, so you can change add a style to it.
36138          * @param {GridView} gridview   The grid view
36139          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36140          */
36141         'rowclass' : true,
36142
36143         /**
36144          * @event render
36145          * Fires when the grid is rendered
36146          * @param {Grid} grid
36147          */
36148         'render' : true
36149     });
36150
36151     Roo.grid.Grid.superclass.constructor.call(this);
36152 };
36153 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36154     
36155     /**
36156      * @cfg {String} ddGroup - drag drop group.
36157      */
36158
36159     /**
36160      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36161      */
36162     minColumnWidth : 25,
36163
36164     /**
36165      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36166      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36167      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36168      */
36169     autoSizeColumns : false,
36170
36171     /**
36172      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36173      */
36174     autoSizeHeaders : true,
36175
36176     /**
36177      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36178      */
36179     monitorWindowResize : true,
36180
36181     /**
36182      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36183      * rows measured to get a columns size. Default is 0 (all rows).
36184      */
36185     maxRowsToMeasure : 0,
36186
36187     /**
36188      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36189      */
36190     trackMouseOver : true,
36191
36192     /**
36193     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36194     */
36195     
36196     /**
36197     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36198     */
36199     enableDragDrop : false,
36200     
36201     /**
36202     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36203     */
36204     enableColumnMove : true,
36205     
36206     /**
36207     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36208     */
36209     enableColumnHide : true,
36210     
36211     /**
36212     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36213     */
36214     enableRowHeightSync : false,
36215     
36216     /**
36217     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36218     */
36219     stripeRows : true,
36220     
36221     /**
36222     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36223     */
36224     autoHeight : false,
36225
36226     /**
36227      * @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.
36228      */
36229     autoExpandColumn : false,
36230
36231     /**
36232     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36233     * Default is 50.
36234     */
36235     autoExpandMin : 50,
36236
36237     /**
36238     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36239     */
36240     autoExpandMax : 1000,
36241
36242     /**
36243     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36244     */
36245     view : null,
36246
36247     /**
36248     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36249     */
36250     loadMask : false,
36251     /**
36252     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36253     */
36254     dropTarget: false,
36255     
36256    
36257     
36258     // private
36259     rendered : false,
36260
36261     /**
36262     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36263     * of a fixed width. Default is false.
36264     */
36265     /**
36266     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36267     */
36268     /**
36269      * Called once after all setup has been completed and the grid is ready to be rendered.
36270      * @return {Roo.grid.Grid} this
36271      */
36272     render : function()
36273     {
36274         var c = this.container;
36275         // try to detect autoHeight/width mode
36276         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36277             this.autoHeight = true;
36278         }
36279         var view = this.getView();
36280         view.init(this);
36281
36282         c.on("click", this.onClick, this);
36283         c.on("dblclick", this.onDblClick, this);
36284         c.on("contextmenu", this.onContextMenu, this);
36285         c.on("keydown", this.onKeyDown, this);
36286         if (Roo.isTouch) {
36287             c.on("touchstart", this.onTouchStart, this);
36288         }
36289
36290         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36291
36292         this.getSelectionModel().init(this);
36293
36294         view.render();
36295
36296         if(this.loadMask){
36297             this.loadMask = new Roo.LoadMask(this.container,
36298                     Roo.apply({store:this.dataSource}, this.loadMask));
36299         }
36300         
36301         
36302         if (this.toolbar && this.toolbar.xtype) {
36303             this.toolbar.container = this.getView().getHeaderPanel(true);
36304             this.toolbar = new Roo.Toolbar(this.toolbar);
36305         }
36306         if (this.footer && this.footer.xtype) {
36307             this.footer.dataSource = this.getDataSource();
36308             this.footer.container = this.getView().getFooterPanel(true);
36309             this.footer = Roo.factory(this.footer, Roo);
36310         }
36311         if (this.dropTarget && this.dropTarget.xtype) {
36312             delete this.dropTarget.xtype;
36313             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36314         }
36315         
36316         
36317         this.rendered = true;
36318         this.fireEvent('render', this);
36319         return this;
36320     },
36321
36322         /**
36323          * Reconfigures the grid to use a different Store and Column Model.
36324          * The View will be bound to the new objects and refreshed.
36325          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36326          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36327          */
36328     reconfigure : function(dataSource, colModel){
36329         if(this.loadMask){
36330             this.loadMask.destroy();
36331             this.loadMask = new Roo.LoadMask(this.container,
36332                     Roo.apply({store:dataSource}, this.loadMask));
36333         }
36334         this.view.bind(dataSource, colModel);
36335         this.dataSource = dataSource;
36336         this.colModel = colModel;
36337         this.view.refresh(true);
36338     },
36339
36340     // private
36341     onKeyDown : function(e){
36342         this.fireEvent("keydown", e);
36343     },
36344
36345     /**
36346      * Destroy this grid.
36347      * @param {Boolean} removeEl True to remove the element
36348      */
36349     destroy : function(removeEl, keepListeners){
36350         if(this.loadMask){
36351             this.loadMask.destroy();
36352         }
36353         var c = this.container;
36354         c.removeAllListeners();
36355         this.view.destroy();
36356         this.colModel.purgeListeners();
36357         if(!keepListeners){
36358             this.purgeListeners();
36359         }
36360         c.update("");
36361         if(removeEl === true){
36362             c.remove();
36363         }
36364     },
36365
36366     // private
36367     processEvent : function(name, e){
36368         // does this fire select???
36369         //Roo.log('grid:processEvent '  + name);
36370         
36371         if (name != 'touchstart' ) {
36372             this.fireEvent(name, e);    
36373         }
36374         
36375         var t = e.getTarget();
36376         var v = this.view;
36377         var header = v.findHeaderIndex(t);
36378         if(header !== false){
36379             var ename = name == 'touchstart' ? 'click' : name;
36380              
36381             this.fireEvent("header" + ename, this, header, e);
36382         }else{
36383             var row = v.findRowIndex(t);
36384             var cell = v.findCellIndex(t);
36385             if (name == 'touchstart') {
36386                 // first touch is always a click.
36387                 // hopefull this happens after selection is updated.?
36388                 name = false;
36389                 
36390                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36391                     var cs = this.selModel.getSelectedCell();
36392                     if (row == cs[0] && cell == cs[1]){
36393                         name = 'dblclick';
36394                     }
36395                 }
36396                 if (typeof(this.selModel.getSelections) != 'undefined') {
36397                     var cs = this.selModel.getSelections();
36398                     var ds = this.dataSource;
36399                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36400                         name = 'dblclick';
36401                     }
36402                 }
36403                 if (!name) {
36404                     return;
36405                 }
36406             }
36407             
36408             
36409             if(row !== false){
36410                 this.fireEvent("row" + name, this, row, e);
36411                 if(cell !== false){
36412                     this.fireEvent("cell" + name, this, row, cell, e);
36413                 }
36414             }
36415         }
36416     },
36417
36418     // private
36419     onClick : function(e){
36420         this.processEvent("click", e);
36421     },
36422    // private
36423     onTouchStart : function(e){
36424         this.processEvent("touchstart", e);
36425     },
36426
36427     // private
36428     onContextMenu : function(e, t){
36429         this.processEvent("contextmenu", e);
36430     },
36431
36432     // private
36433     onDblClick : function(e){
36434         this.processEvent("dblclick", e);
36435     },
36436
36437     // private
36438     walkCells : function(row, col, step, fn, scope){
36439         var cm = this.colModel, clen = cm.getColumnCount();
36440         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36441         if(step < 0){
36442             if(col < 0){
36443                 row--;
36444                 first = false;
36445             }
36446             while(row >= 0){
36447                 if(!first){
36448                     col = clen-1;
36449                 }
36450                 first = false;
36451                 while(col >= 0){
36452                     if(fn.call(scope || this, row, col, cm) === true){
36453                         return [row, col];
36454                     }
36455                     col--;
36456                 }
36457                 row--;
36458             }
36459         } else {
36460             if(col >= clen){
36461                 row++;
36462                 first = false;
36463             }
36464             while(row < rlen){
36465                 if(!first){
36466                     col = 0;
36467                 }
36468                 first = false;
36469                 while(col < clen){
36470                     if(fn.call(scope || this, row, col, cm) === true){
36471                         return [row, col];
36472                     }
36473                     col++;
36474                 }
36475                 row++;
36476             }
36477         }
36478         return null;
36479     },
36480
36481     // private
36482     getSelections : function(){
36483         return this.selModel.getSelections();
36484     },
36485
36486     /**
36487      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36488      * but if manual update is required this method will initiate it.
36489      */
36490     autoSize : function(){
36491         if(this.rendered){
36492             this.view.layout();
36493             if(this.view.adjustForScroll){
36494                 this.view.adjustForScroll();
36495             }
36496         }
36497     },
36498
36499     /**
36500      * Returns the grid's underlying element.
36501      * @return {Element} The element
36502      */
36503     getGridEl : function(){
36504         return this.container;
36505     },
36506
36507     // private for compatibility, overridden by editor grid
36508     stopEditing : function(){},
36509
36510     /**
36511      * Returns the grid's SelectionModel.
36512      * @return {SelectionModel}
36513      */
36514     getSelectionModel : function(){
36515         if(!this.selModel){
36516             this.selModel = new Roo.grid.RowSelectionModel();
36517         }
36518         return this.selModel;
36519     },
36520
36521     /**
36522      * Returns the grid's DataSource.
36523      * @return {DataSource}
36524      */
36525     getDataSource : function(){
36526         return this.dataSource;
36527     },
36528
36529     /**
36530      * Returns the grid's ColumnModel.
36531      * @return {ColumnModel}
36532      */
36533     getColumnModel : function(){
36534         return this.colModel;
36535     },
36536
36537     /**
36538      * Returns the grid's GridView object.
36539      * @return {GridView}
36540      */
36541     getView : function(){
36542         if(!this.view){
36543             this.view = new Roo.grid.GridView(this.viewConfig);
36544         }
36545         return this.view;
36546     },
36547     /**
36548      * Called to get grid's drag proxy text, by default returns this.ddText.
36549      * @return {String}
36550      */
36551     getDragDropText : function(){
36552         var count = this.selModel.getCount();
36553         return String.format(this.ddText, count, count == 1 ? '' : 's');
36554     }
36555 });
36556 /**
36557  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36558  * %0 is replaced with the number of selected rows.
36559  * @type String
36560  */
36561 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36562  * Based on:
36563  * Ext JS Library 1.1.1
36564  * Copyright(c) 2006-2007, Ext JS, LLC.
36565  *
36566  * Originally Released Under LGPL - original licence link has changed is not relivant.
36567  *
36568  * Fork - LGPL
36569  * <script type="text/javascript">
36570  */
36571  
36572 Roo.grid.AbstractGridView = function(){
36573         this.grid = null;
36574         
36575         this.events = {
36576             "beforerowremoved" : true,
36577             "beforerowsinserted" : true,
36578             "beforerefresh" : true,
36579             "rowremoved" : true,
36580             "rowsinserted" : true,
36581             "rowupdated" : true,
36582             "refresh" : true
36583         };
36584     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36585 };
36586
36587 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36588     rowClass : "x-grid-row",
36589     cellClass : "x-grid-cell",
36590     tdClass : "x-grid-td",
36591     hdClass : "x-grid-hd",
36592     splitClass : "x-grid-hd-split",
36593     
36594     init: function(grid){
36595         this.grid = grid;
36596                 var cid = this.grid.getGridEl().id;
36597         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36598         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36599         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36600         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36601         },
36602         
36603     getColumnRenderers : function(){
36604         var renderers = [];
36605         var cm = this.grid.colModel;
36606         var colCount = cm.getColumnCount();
36607         for(var i = 0; i < colCount; i++){
36608             renderers[i] = cm.getRenderer(i);
36609         }
36610         return renderers;
36611     },
36612     
36613     getColumnIds : function(){
36614         var ids = [];
36615         var cm = this.grid.colModel;
36616         var colCount = cm.getColumnCount();
36617         for(var i = 0; i < colCount; i++){
36618             ids[i] = cm.getColumnId(i);
36619         }
36620         return ids;
36621     },
36622     
36623     getDataIndexes : function(){
36624         if(!this.indexMap){
36625             this.indexMap = this.buildIndexMap();
36626         }
36627         return this.indexMap.colToData;
36628     },
36629     
36630     getColumnIndexByDataIndex : function(dataIndex){
36631         if(!this.indexMap){
36632             this.indexMap = this.buildIndexMap();
36633         }
36634         return this.indexMap.dataToCol[dataIndex];
36635     },
36636     
36637     /**
36638      * Set a css style for a column dynamically. 
36639      * @param {Number} colIndex The index of the column
36640      * @param {String} name The css property name
36641      * @param {String} value The css value
36642      */
36643     setCSSStyle : function(colIndex, name, value){
36644         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36645         Roo.util.CSS.updateRule(selector, name, value);
36646     },
36647     
36648     generateRules : function(cm){
36649         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36650         Roo.util.CSS.removeStyleSheet(rulesId);
36651         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36652             var cid = cm.getColumnId(i);
36653             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36654                          this.tdSelector, cid, " {\n}\n",
36655                          this.hdSelector, cid, " {\n}\n",
36656                          this.splitSelector, cid, " {\n}\n");
36657         }
36658         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36659     }
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670
36671 // private
36672 // This is a support class used internally by the Grid components
36673 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36674     this.grid = grid;
36675     this.view = grid.getView();
36676     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36677     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36678     if(hd2){
36679         this.setHandleElId(Roo.id(hd));
36680         this.setOuterHandleElId(Roo.id(hd2));
36681     }
36682     this.scroll = false;
36683 };
36684 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36685     maxDragWidth: 120,
36686     getDragData : function(e){
36687         var t = Roo.lib.Event.getTarget(e);
36688         var h = this.view.findHeaderCell(t);
36689         if(h){
36690             return {ddel: h.firstChild, header:h};
36691         }
36692         return false;
36693     },
36694
36695     onInitDrag : function(e){
36696         this.view.headersDisabled = true;
36697         var clone = this.dragData.ddel.cloneNode(true);
36698         clone.id = Roo.id();
36699         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36700         this.proxy.update(clone);
36701         return true;
36702     },
36703
36704     afterValidDrop : function(){
36705         var v = this.view;
36706         setTimeout(function(){
36707             v.headersDisabled = false;
36708         }, 50);
36709     },
36710
36711     afterInvalidDrop : function(){
36712         var v = this.view;
36713         setTimeout(function(){
36714             v.headersDisabled = false;
36715         }, 50);
36716     }
36717 });
36718 /*
36719  * Based on:
36720  * Ext JS Library 1.1.1
36721  * Copyright(c) 2006-2007, Ext JS, LLC.
36722  *
36723  * Originally Released Under LGPL - original licence link has changed is not relivant.
36724  *
36725  * Fork - LGPL
36726  * <script type="text/javascript">
36727  */
36728 // private
36729 // This is a support class used internally by the Grid components
36730 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36731     this.grid = grid;
36732     this.view = grid.getView();
36733     // split the proxies so they don't interfere with mouse events
36734     this.proxyTop = Roo.DomHelper.append(document.body, {
36735         cls:"col-move-top", html:"&#160;"
36736     }, true);
36737     this.proxyBottom = Roo.DomHelper.append(document.body, {
36738         cls:"col-move-bottom", html:"&#160;"
36739     }, true);
36740     this.proxyTop.hide = this.proxyBottom.hide = function(){
36741         this.setLeftTop(-100,-100);
36742         this.setStyle("visibility", "hidden");
36743     };
36744     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36745     // temporarily disabled
36746     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36747     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36748 };
36749 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36750     proxyOffsets : [-4, -9],
36751     fly: Roo.Element.fly,
36752
36753     getTargetFromEvent : function(e){
36754         var t = Roo.lib.Event.getTarget(e);
36755         var cindex = this.view.findCellIndex(t);
36756         if(cindex !== false){
36757             return this.view.getHeaderCell(cindex);
36758         }
36759         return null;
36760     },
36761
36762     nextVisible : function(h){
36763         var v = this.view, cm = this.grid.colModel;
36764         h = h.nextSibling;
36765         while(h){
36766             if(!cm.isHidden(v.getCellIndex(h))){
36767                 return h;
36768             }
36769             h = h.nextSibling;
36770         }
36771         return null;
36772     },
36773
36774     prevVisible : function(h){
36775         var v = this.view, cm = this.grid.colModel;
36776         h = h.prevSibling;
36777         while(h){
36778             if(!cm.isHidden(v.getCellIndex(h))){
36779                 return h;
36780             }
36781             h = h.prevSibling;
36782         }
36783         return null;
36784     },
36785
36786     positionIndicator : function(h, n, e){
36787         var x = Roo.lib.Event.getPageX(e);
36788         var r = Roo.lib.Dom.getRegion(n.firstChild);
36789         var px, pt, py = r.top + this.proxyOffsets[1];
36790         if((r.right - x) <= (r.right-r.left)/2){
36791             px = r.right+this.view.borderWidth;
36792             pt = "after";
36793         }else{
36794             px = r.left;
36795             pt = "before";
36796         }
36797         var oldIndex = this.view.getCellIndex(h);
36798         var newIndex = this.view.getCellIndex(n);
36799
36800         if(this.grid.colModel.isFixed(newIndex)){
36801             return false;
36802         }
36803
36804         var locked = this.grid.colModel.isLocked(newIndex);
36805
36806         if(pt == "after"){
36807             newIndex++;
36808         }
36809         if(oldIndex < newIndex){
36810             newIndex--;
36811         }
36812         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36813             return false;
36814         }
36815         px +=  this.proxyOffsets[0];
36816         this.proxyTop.setLeftTop(px, py);
36817         this.proxyTop.show();
36818         if(!this.bottomOffset){
36819             this.bottomOffset = this.view.mainHd.getHeight();
36820         }
36821         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36822         this.proxyBottom.show();
36823         return pt;
36824     },
36825
36826     onNodeEnter : function(n, dd, e, data){
36827         if(data.header != n){
36828             this.positionIndicator(data.header, n, e);
36829         }
36830     },
36831
36832     onNodeOver : function(n, dd, e, data){
36833         var result = false;
36834         if(data.header != n){
36835             result = this.positionIndicator(data.header, n, e);
36836         }
36837         if(!result){
36838             this.proxyTop.hide();
36839             this.proxyBottom.hide();
36840         }
36841         return result ? this.dropAllowed : this.dropNotAllowed;
36842     },
36843
36844     onNodeOut : function(n, dd, e, data){
36845         this.proxyTop.hide();
36846         this.proxyBottom.hide();
36847     },
36848
36849     onNodeDrop : function(n, dd, e, data){
36850         var h = data.header;
36851         if(h != n){
36852             var cm = this.grid.colModel;
36853             var x = Roo.lib.Event.getPageX(e);
36854             var r = Roo.lib.Dom.getRegion(n.firstChild);
36855             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36856             var oldIndex = this.view.getCellIndex(h);
36857             var newIndex = this.view.getCellIndex(n);
36858             var locked = cm.isLocked(newIndex);
36859             if(pt == "after"){
36860                 newIndex++;
36861             }
36862             if(oldIndex < newIndex){
36863                 newIndex--;
36864             }
36865             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36866                 return false;
36867             }
36868             cm.setLocked(oldIndex, locked, true);
36869             cm.moveColumn(oldIndex, newIndex);
36870             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36871             return true;
36872         }
36873         return false;
36874     }
36875 });
36876 /*
36877  * Based on:
36878  * Ext JS Library 1.1.1
36879  * Copyright(c) 2006-2007, Ext JS, LLC.
36880  *
36881  * Originally Released Under LGPL - original licence link has changed is not relivant.
36882  *
36883  * Fork - LGPL
36884  * <script type="text/javascript">
36885  */
36886   
36887 /**
36888  * @class Roo.grid.GridView
36889  * @extends Roo.util.Observable
36890  *
36891  * @constructor
36892  * @param {Object} config
36893  */
36894 Roo.grid.GridView = function(config){
36895     Roo.grid.GridView.superclass.constructor.call(this);
36896     this.el = null;
36897
36898     Roo.apply(this, config);
36899 };
36900
36901 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36902
36903     unselectable :  'unselectable="on"',
36904     unselectableCls :  'x-unselectable',
36905     
36906     
36907     rowClass : "x-grid-row",
36908
36909     cellClass : "x-grid-col",
36910
36911     tdClass : "x-grid-td",
36912
36913     hdClass : "x-grid-hd",
36914
36915     splitClass : "x-grid-split",
36916
36917     sortClasses : ["sort-asc", "sort-desc"],
36918
36919     enableMoveAnim : false,
36920
36921     hlColor: "C3DAF9",
36922
36923     dh : Roo.DomHelper,
36924
36925     fly : Roo.Element.fly,
36926
36927     css : Roo.util.CSS,
36928
36929     borderWidth: 1,
36930
36931     splitOffset: 3,
36932
36933     scrollIncrement : 22,
36934
36935     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36936
36937     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36938
36939     bind : function(ds, cm){
36940         if(this.ds){
36941             this.ds.un("load", this.onLoad, this);
36942             this.ds.un("datachanged", this.onDataChange, this);
36943             this.ds.un("add", this.onAdd, this);
36944             this.ds.un("remove", this.onRemove, this);
36945             this.ds.un("update", this.onUpdate, this);
36946             this.ds.un("clear", this.onClear, this);
36947         }
36948         if(ds){
36949             ds.on("load", this.onLoad, this);
36950             ds.on("datachanged", this.onDataChange, this);
36951             ds.on("add", this.onAdd, this);
36952             ds.on("remove", this.onRemove, this);
36953             ds.on("update", this.onUpdate, this);
36954             ds.on("clear", this.onClear, this);
36955         }
36956         this.ds = ds;
36957
36958         if(this.cm){
36959             this.cm.un("widthchange", this.onColWidthChange, this);
36960             this.cm.un("headerchange", this.onHeaderChange, this);
36961             this.cm.un("hiddenchange", this.onHiddenChange, this);
36962             this.cm.un("columnmoved", this.onColumnMove, this);
36963             this.cm.un("columnlockchange", this.onColumnLock, this);
36964         }
36965         if(cm){
36966             this.generateRules(cm);
36967             cm.on("widthchange", this.onColWidthChange, this);
36968             cm.on("headerchange", this.onHeaderChange, this);
36969             cm.on("hiddenchange", this.onHiddenChange, this);
36970             cm.on("columnmoved", this.onColumnMove, this);
36971             cm.on("columnlockchange", this.onColumnLock, this);
36972         }
36973         this.cm = cm;
36974     },
36975
36976     init: function(grid){
36977         Roo.grid.GridView.superclass.init.call(this, grid);
36978
36979         this.bind(grid.dataSource, grid.colModel);
36980
36981         grid.on("headerclick", this.handleHeaderClick, this);
36982
36983         if(grid.trackMouseOver){
36984             grid.on("mouseover", this.onRowOver, this);
36985             grid.on("mouseout", this.onRowOut, this);
36986         }
36987         grid.cancelTextSelection = function(){};
36988         this.gridId = grid.id;
36989
36990         var tpls = this.templates || {};
36991
36992         if(!tpls.master){
36993             tpls.master = new Roo.Template(
36994                '<div class="x-grid" hidefocus="true">',
36995                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36996                   '<div class="x-grid-topbar"></div>',
36997                   '<div class="x-grid-scroller"><div></div></div>',
36998                   '<div class="x-grid-locked">',
36999                       '<div class="x-grid-header">{lockedHeader}</div>',
37000                       '<div class="x-grid-body">{lockedBody}</div>',
37001                   "</div>",
37002                   '<div class="x-grid-viewport">',
37003                       '<div class="x-grid-header">{header}</div>',
37004                       '<div class="x-grid-body">{body}</div>',
37005                   "</div>",
37006                   '<div class="x-grid-bottombar"></div>',
37007                  
37008                   '<div class="x-grid-resize-proxy">&#160;</div>',
37009                "</div>"
37010             );
37011             tpls.master.disableformats = true;
37012         }
37013
37014         if(!tpls.header){
37015             tpls.header = new Roo.Template(
37016                '<table border="0" cellspacing="0" cellpadding="0">',
37017                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37018                "</table>{splits}"
37019             );
37020             tpls.header.disableformats = true;
37021         }
37022         tpls.header.compile();
37023
37024         if(!tpls.hcell){
37025             tpls.hcell = new Roo.Template(
37026                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37027                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37028                 "</div></td>"
37029              );
37030              tpls.hcell.disableFormats = true;
37031         }
37032         tpls.hcell.compile();
37033
37034         if(!tpls.hsplit){
37035             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37036                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37037             tpls.hsplit.disableFormats = true;
37038         }
37039         tpls.hsplit.compile();
37040
37041         if(!tpls.body){
37042             tpls.body = new Roo.Template(
37043                '<table border="0" cellspacing="0" cellpadding="0">',
37044                "<tbody>{rows}</tbody>",
37045                "</table>"
37046             );
37047             tpls.body.disableFormats = true;
37048         }
37049         tpls.body.compile();
37050
37051         if(!tpls.row){
37052             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37053             tpls.row.disableFormats = true;
37054         }
37055         tpls.row.compile();
37056
37057         if(!tpls.cell){
37058             tpls.cell = new Roo.Template(
37059                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37060                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37061                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37062                 "</td>"
37063             );
37064             tpls.cell.disableFormats = true;
37065         }
37066         tpls.cell.compile();
37067
37068         this.templates = tpls;
37069     },
37070
37071     // remap these for backwards compat
37072     onColWidthChange : function(){
37073         this.updateColumns.apply(this, arguments);
37074     },
37075     onHeaderChange : function(){
37076         this.updateHeaders.apply(this, arguments);
37077     }, 
37078     onHiddenChange : function(){
37079         this.handleHiddenChange.apply(this, arguments);
37080     },
37081     onColumnMove : function(){
37082         this.handleColumnMove.apply(this, arguments);
37083     },
37084     onColumnLock : function(){
37085         this.handleLockChange.apply(this, arguments);
37086     },
37087
37088     onDataChange : function(){
37089         this.refresh();
37090         this.updateHeaderSortState();
37091     },
37092
37093     onClear : function(){
37094         this.refresh();
37095     },
37096
37097     onUpdate : function(ds, record){
37098         this.refreshRow(record);
37099     },
37100
37101     refreshRow : function(record){
37102         var ds = this.ds, index;
37103         if(typeof record == 'number'){
37104             index = record;
37105             record = ds.getAt(index);
37106         }else{
37107             index = ds.indexOf(record);
37108         }
37109         this.insertRows(ds, index, index, true);
37110         this.onRemove(ds, record, index+1, true);
37111         this.syncRowHeights(index, index);
37112         this.layout();
37113         this.fireEvent("rowupdated", this, index, record);
37114     },
37115
37116     onAdd : function(ds, records, index){
37117         this.insertRows(ds, index, index + (records.length-1));
37118     },
37119
37120     onRemove : function(ds, record, index, isUpdate){
37121         if(isUpdate !== true){
37122             this.fireEvent("beforerowremoved", this, index, record);
37123         }
37124         var bt = this.getBodyTable(), lt = this.getLockedTable();
37125         if(bt.rows[index]){
37126             bt.firstChild.removeChild(bt.rows[index]);
37127         }
37128         if(lt.rows[index]){
37129             lt.firstChild.removeChild(lt.rows[index]);
37130         }
37131         if(isUpdate !== true){
37132             this.stripeRows(index);
37133             this.syncRowHeights(index, index);
37134             this.layout();
37135             this.fireEvent("rowremoved", this, index, record);
37136         }
37137     },
37138
37139     onLoad : function(){
37140         this.scrollToTop();
37141     },
37142
37143     /**
37144      * Scrolls the grid to the top
37145      */
37146     scrollToTop : function(){
37147         if(this.scroller){
37148             this.scroller.dom.scrollTop = 0;
37149             this.syncScroll();
37150         }
37151     },
37152
37153     /**
37154      * Gets a panel in the header of the grid that can be used for toolbars etc.
37155      * After modifying the contents of this panel a call to grid.autoSize() may be
37156      * required to register any changes in size.
37157      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37158      * @return Roo.Element
37159      */
37160     getHeaderPanel : function(doShow){
37161         if(doShow){
37162             this.headerPanel.show();
37163         }
37164         return this.headerPanel;
37165     },
37166
37167     /**
37168      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37169      * After modifying the contents of this panel a call to grid.autoSize() may be
37170      * required to register any changes in size.
37171      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37172      * @return Roo.Element
37173      */
37174     getFooterPanel : function(doShow){
37175         if(doShow){
37176             this.footerPanel.show();
37177         }
37178         return this.footerPanel;
37179     },
37180
37181     initElements : function(){
37182         var E = Roo.Element;
37183         var el = this.grid.getGridEl().dom.firstChild;
37184         var cs = el.childNodes;
37185
37186         this.el = new E(el);
37187         
37188          this.focusEl = new E(el.firstChild);
37189         this.focusEl.swallowEvent("click", true);
37190         
37191         this.headerPanel = new E(cs[1]);
37192         this.headerPanel.enableDisplayMode("block");
37193
37194         this.scroller = new E(cs[2]);
37195         this.scrollSizer = new E(this.scroller.dom.firstChild);
37196
37197         this.lockedWrap = new E(cs[3]);
37198         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37199         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37200
37201         this.mainWrap = new E(cs[4]);
37202         this.mainHd = new E(this.mainWrap.dom.firstChild);
37203         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37204
37205         this.footerPanel = new E(cs[5]);
37206         this.footerPanel.enableDisplayMode("block");
37207
37208         this.resizeProxy = new E(cs[6]);
37209
37210         this.headerSelector = String.format(
37211            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37212            this.lockedHd.id, this.mainHd.id
37213         );
37214
37215         this.splitterSelector = String.format(
37216            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37217            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37218         );
37219     },
37220     idToCssName : function(s)
37221     {
37222         return s.replace(/[^a-z0-9]+/ig, '-');
37223     },
37224
37225     getHeaderCell : function(index){
37226         return Roo.DomQuery.select(this.headerSelector)[index];
37227     },
37228
37229     getHeaderCellMeasure : function(index){
37230         return this.getHeaderCell(index).firstChild;
37231     },
37232
37233     getHeaderCellText : function(index){
37234         return this.getHeaderCell(index).firstChild.firstChild;
37235     },
37236
37237     getLockedTable : function(){
37238         return this.lockedBody.dom.firstChild;
37239     },
37240
37241     getBodyTable : function(){
37242         return this.mainBody.dom.firstChild;
37243     },
37244
37245     getLockedRow : function(index){
37246         return this.getLockedTable().rows[index];
37247     },
37248
37249     getRow : function(index){
37250         return this.getBodyTable().rows[index];
37251     },
37252
37253     getRowComposite : function(index){
37254         if(!this.rowEl){
37255             this.rowEl = new Roo.CompositeElementLite();
37256         }
37257         var els = [], lrow, mrow;
37258         if(lrow = this.getLockedRow(index)){
37259             els.push(lrow);
37260         }
37261         if(mrow = this.getRow(index)){
37262             els.push(mrow);
37263         }
37264         this.rowEl.elements = els;
37265         return this.rowEl;
37266     },
37267     /**
37268      * Gets the 'td' of the cell
37269      * 
37270      * @param {Integer} rowIndex row to select
37271      * @param {Integer} colIndex column to select
37272      * 
37273      * @return {Object} 
37274      */
37275     getCell : function(rowIndex, colIndex){
37276         var locked = this.cm.getLockedCount();
37277         var source;
37278         if(colIndex < locked){
37279             source = this.lockedBody.dom.firstChild;
37280         }else{
37281             source = this.mainBody.dom.firstChild;
37282             colIndex -= locked;
37283         }
37284         return source.rows[rowIndex].childNodes[colIndex];
37285     },
37286
37287     getCellText : function(rowIndex, colIndex){
37288         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37289     },
37290
37291     getCellBox : function(cell){
37292         var b = this.fly(cell).getBox();
37293         if(Roo.isOpera){ // opera fails to report the Y
37294             b.y = cell.offsetTop + this.mainBody.getY();
37295         }
37296         return b;
37297     },
37298
37299     getCellIndex : function(cell){
37300         var id = String(cell.className).match(this.cellRE);
37301         if(id){
37302             return parseInt(id[1], 10);
37303         }
37304         return 0;
37305     },
37306
37307     findHeaderIndex : function(n){
37308         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37309         return r ? this.getCellIndex(r) : false;
37310     },
37311
37312     findHeaderCell : function(n){
37313         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37314         return r ? r : false;
37315     },
37316
37317     findRowIndex : function(n){
37318         if(!n){
37319             return false;
37320         }
37321         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37322         return r ? r.rowIndex : false;
37323     },
37324
37325     findCellIndex : function(node){
37326         var stop = this.el.dom;
37327         while(node && node != stop){
37328             if(this.findRE.test(node.className)){
37329                 return this.getCellIndex(node);
37330             }
37331             node = node.parentNode;
37332         }
37333         return false;
37334     },
37335
37336     getColumnId : function(index){
37337         return this.cm.getColumnId(index);
37338     },
37339
37340     getSplitters : function()
37341     {
37342         if(this.splitterSelector){
37343            return Roo.DomQuery.select(this.splitterSelector);
37344         }else{
37345             return null;
37346       }
37347     },
37348
37349     getSplitter : function(index){
37350         return this.getSplitters()[index];
37351     },
37352
37353     onRowOver : function(e, t){
37354         var row;
37355         if((row = this.findRowIndex(t)) !== false){
37356             this.getRowComposite(row).addClass("x-grid-row-over");
37357         }
37358     },
37359
37360     onRowOut : function(e, t){
37361         var row;
37362         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37363             this.getRowComposite(row).removeClass("x-grid-row-over");
37364         }
37365     },
37366
37367     renderHeaders : function(){
37368         var cm = this.cm;
37369         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37370         var cb = [], lb = [], sb = [], lsb = [], p = {};
37371         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37372             p.cellId = "x-grid-hd-0-" + i;
37373             p.splitId = "x-grid-csplit-0-" + i;
37374             p.id = cm.getColumnId(i);
37375             p.title = cm.getColumnTooltip(i) || "";
37376             p.value = cm.getColumnHeader(i) || "";
37377             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37378             if(!cm.isLocked(i)){
37379                 cb[cb.length] = ct.apply(p);
37380                 sb[sb.length] = st.apply(p);
37381             }else{
37382                 lb[lb.length] = ct.apply(p);
37383                 lsb[lsb.length] = st.apply(p);
37384             }
37385         }
37386         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37387                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37388     },
37389
37390     updateHeaders : function(){
37391         var html = this.renderHeaders();
37392         this.lockedHd.update(html[0]);
37393         this.mainHd.update(html[1]);
37394     },
37395
37396     /**
37397      * Focuses the specified row.
37398      * @param {Number} row The row index
37399      */
37400     focusRow : function(row)
37401     {
37402         //Roo.log('GridView.focusRow');
37403         var x = this.scroller.dom.scrollLeft;
37404         this.focusCell(row, 0, false);
37405         this.scroller.dom.scrollLeft = x;
37406     },
37407
37408     /**
37409      * Focuses the specified cell.
37410      * @param {Number} row The row index
37411      * @param {Number} col The column index
37412      * @param {Boolean} hscroll false to disable horizontal scrolling
37413      */
37414     focusCell : function(row, col, hscroll)
37415     {
37416         //Roo.log('GridView.focusCell');
37417         var el = this.ensureVisible(row, col, hscroll);
37418         this.focusEl.alignTo(el, "tl-tl");
37419         if(Roo.isGecko){
37420             this.focusEl.focus();
37421         }else{
37422             this.focusEl.focus.defer(1, this.focusEl);
37423         }
37424     },
37425
37426     /**
37427      * Scrolls the specified cell into view
37428      * @param {Number} row The row index
37429      * @param {Number} col The column index
37430      * @param {Boolean} hscroll false to disable horizontal scrolling
37431      */
37432     ensureVisible : function(row, col, hscroll)
37433     {
37434         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37435         //return null; //disable for testing.
37436         if(typeof row != "number"){
37437             row = row.rowIndex;
37438         }
37439         if(row < 0 && row >= this.ds.getCount()){
37440             return  null;
37441         }
37442         col = (col !== undefined ? col : 0);
37443         var cm = this.grid.colModel;
37444         while(cm.isHidden(col)){
37445             col++;
37446         }
37447
37448         var el = this.getCell(row, col);
37449         if(!el){
37450             return null;
37451         }
37452         var c = this.scroller.dom;
37453
37454         var ctop = parseInt(el.offsetTop, 10);
37455         var cleft = parseInt(el.offsetLeft, 10);
37456         var cbot = ctop + el.offsetHeight;
37457         var cright = cleft + el.offsetWidth;
37458         
37459         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37460         var stop = parseInt(c.scrollTop, 10);
37461         var sleft = parseInt(c.scrollLeft, 10);
37462         var sbot = stop + ch;
37463         var sright = sleft + c.clientWidth;
37464         /*
37465         Roo.log('GridView.ensureVisible:' +
37466                 ' ctop:' + ctop +
37467                 ' c.clientHeight:' + c.clientHeight +
37468                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37469                 ' stop:' + stop +
37470                 ' cbot:' + cbot +
37471                 ' sbot:' + sbot +
37472                 ' ch:' + ch  
37473                 );
37474         */
37475         if(ctop < stop){
37476              c.scrollTop = ctop;
37477             //Roo.log("set scrolltop to ctop DISABLE?");
37478         }else if(cbot > sbot){
37479             //Roo.log("set scrolltop to cbot-ch");
37480             c.scrollTop = cbot-ch;
37481         }
37482         
37483         if(hscroll !== false){
37484             if(cleft < sleft){
37485                 c.scrollLeft = cleft;
37486             }else if(cright > sright){
37487                 c.scrollLeft = cright-c.clientWidth;
37488             }
37489         }
37490          
37491         return el;
37492     },
37493
37494     updateColumns : function(){
37495         this.grid.stopEditing();
37496         var cm = this.grid.colModel, colIds = this.getColumnIds();
37497         //var totalWidth = cm.getTotalWidth();
37498         var pos = 0;
37499         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37500             //if(cm.isHidden(i)) continue;
37501             var w = cm.getColumnWidth(i);
37502             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37503             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37504         }
37505         this.updateSplitters();
37506     },
37507
37508     generateRules : function(cm){
37509         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37510         Roo.util.CSS.removeStyleSheet(rulesId);
37511         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37512             var cid = cm.getColumnId(i);
37513             var align = '';
37514             if(cm.config[i].align){
37515                 align = 'text-align:'+cm.config[i].align+';';
37516             }
37517             var hidden = '';
37518             if(cm.isHidden(i)){
37519                 hidden = 'display:none;';
37520             }
37521             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37522             ruleBuf.push(
37523                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37524                     this.hdSelector, cid, " {\n", align, width, "}\n",
37525                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37526                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37527         }
37528         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37529     },
37530
37531     updateSplitters : function(){
37532         var cm = this.cm, s = this.getSplitters();
37533         if(s){ // splitters not created yet
37534             var pos = 0, locked = true;
37535             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37536                 if(cm.isHidden(i)) continue;
37537                 var w = cm.getColumnWidth(i); // make sure it's a number
37538                 if(!cm.isLocked(i) && locked){
37539                     pos = 0;
37540                     locked = false;
37541                 }
37542                 pos += w;
37543                 s[i].style.left = (pos-this.splitOffset) + "px";
37544             }
37545         }
37546     },
37547
37548     handleHiddenChange : function(colModel, colIndex, hidden){
37549         if(hidden){
37550             this.hideColumn(colIndex);
37551         }else{
37552             this.unhideColumn(colIndex);
37553         }
37554     },
37555
37556     hideColumn : function(colIndex){
37557         var cid = this.getColumnId(colIndex);
37558         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37559         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37560         if(Roo.isSafari){
37561             this.updateHeaders();
37562         }
37563         this.updateSplitters();
37564         this.layout();
37565     },
37566
37567     unhideColumn : function(colIndex){
37568         var cid = this.getColumnId(colIndex);
37569         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37570         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37571
37572         if(Roo.isSafari){
37573             this.updateHeaders();
37574         }
37575         this.updateSplitters();
37576         this.layout();
37577     },
37578
37579     insertRows : function(dm, firstRow, lastRow, isUpdate){
37580         if(firstRow == 0 && lastRow == dm.getCount()-1){
37581             this.refresh();
37582         }else{
37583             if(!isUpdate){
37584                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37585             }
37586             var s = this.getScrollState();
37587             var markup = this.renderRows(firstRow, lastRow);
37588             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37589             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37590             this.restoreScroll(s);
37591             if(!isUpdate){
37592                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37593                 this.syncRowHeights(firstRow, lastRow);
37594                 this.stripeRows(firstRow);
37595                 this.layout();
37596             }
37597         }
37598     },
37599
37600     bufferRows : function(markup, target, index){
37601         var before = null, trows = target.rows, tbody = target.tBodies[0];
37602         if(index < trows.length){
37603             before = trows[index];
37604         }
37605         var b = document.createElement("div");
37606         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37607         var rows = b.firstChild.rows;
37608         for(var i = 0, len = rows.length; i < len; i++){
37609             if(before){
37610                 tbody.insertBefore(rows[0], before);
37611             }else{
37612                 tbody.appendChild(rows[0]);
37613             }
37614         }
37615         b.innerHTML = "";
37616         b = null;
37617     },
37618
37619     deleteRows : function(dm, firstRow, lastRow){
37620         if(dm.getRowCount()<1){
37621             this.fireEvent("beforerefresh", this);
37622             this.mainBody.update("");
37623             this.lockedBody.update("");
37624             this.fireEvent("refresh", this);
37625         }else{
37626             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37627             var bt = this.getBodyTable();
37628             var tbody = bt.firstChild;
37629             var rows = bt.rows;
37630             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37631                 tbody.removeChild(rows[firstRow]);
37632             }
37633             this.stripeRows(firstRow);
37634             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37635         }
37636     },
37637
37638     updateRows : function(dataSource, firstRow, lastRow){
37639         var s = this.getScrollState();
37640         this.refresh();
37641         this.restoreScroll(s);
37642     },
37643
37644     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37645         if(!noRefresh){
37646            this.refresh();
37647         }
37648         this.updateHeaderSortState();
37649     },
37650
37651     getScrollState : function(){
37652         
37653         var sb = this.scroller.dom;
37654         return {left: sb.scrollLeft, top: sb.scrollTop};
37655     },
37656
37657     stripeRows : function(startRow){
37658         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37659             return;
37660         }
37661         startRow = startRow || 0;
37662         var rows = this.getBodyTable().rows;
37663         var lrows = this.getLockedTable().rows;
37664         var cls = ' x-grid-row-alt ';
37665         for(var i = startRow, len = rows.length; i < len; i++){
37666             var row = rows[i], lrow = lrows[i];
37667             var isAlt = ((i+1) % 2 == 0);
37668             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37669             if(isAlt == hasAlt){
37670                 continue;
37671             }
37672             if(isAlt){
37673                 row.className += " x-grid-row-alt";
37674             }else{
37675                 row.className = row.className.replace("x-grid-row-alt", "");
37676             }
37677             if(lrow){
37678                 lrow.className = row.className;
37679             }
37680         }
37681     },
37682
37683     restoreScroll : function(state){
37684         //Roo.log('GridView.restoreScroll');
37685         var sb = this.scroller.dom;
37686         sb.scrollLeft = state.left;
37687         sb.scrollTop = state.top;
37688         this.syncScroll();
37689     },
37690
37691     syncScroll : function(){
37692         //Roo.log('GridView.syncScroll');
37693         var sb = this.scroller.dom;
37694         var sh = this.mainHd.dom;
37695         var bs = this.mainBody.dom;
37696         var lv = this.lockedBody.dom;
37697         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37698         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37699     },
37700
37701     handleScroll : function(e){
37702         this.syncScroll();
37703         var sb = this.scroller.dom;
37704         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37705         e.stopEvent();
37706     },
37707
37708     handleWheel : function(e){
37709         var d = e.getWheelDelta();
37710         this.scroller.dom.scrollTop -= d*22;
37711         // set this here to prevent jumpy scrolling on large tables
37712         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37713         e.stopEvent();
37714     },
37715
37716     renderRows : function(startRow, endRow){
37717         // pull in all the crap needed to render rows
37718         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37719         var colCount = cm.getColumnCount();
37720
37721         if(ds.getCount() < 1){
37722             return ["", ""];
37723         }
37724
37725         // build a map for all the columns
37726         var cs = [];
37727         for(var i = 0; i < colCount; i++){
37728             var name = cm.getDataIndex(i);
37729             cs[i] = {
37730                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37731                 renderer : cm.getRenderer(i),
37732                 id : cm.getColumnId(i),
37733                 locked : cm.isLocked(i)
37734             };
37735         }
37736
37737         startRow = startRow || 0;
37738         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37739
37740         // records to render
37741         var rs = ds.getRange(startRow, endRow);
37742
37743         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37744     },
37745
37746     // As much as I hate to duplicate code, this was branched because FireFox really hates
37747     // [].join("") on strings. The performance difference was substantial enough to
37748     // branch this function
37749     doRender : Roo.isGecko ?
37750             function(cs, rs, ds, startRow, colCount, stripe){
37751                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37752                 // buffers
37753                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37754                 
37755                 var hasListener = this.grid.hasListener('rowclass');
37756                 var rowcfg = {};
37757                 for(var j = 0, len = rs.length; j < len; j++){
37758                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37759                     for(var i = 0; i < colCount; i++){
37760                         c = cs[i];
37761                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37762                         p.id = c.id;
37763                         p.css = p.attr = "";
37764                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37765                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37766                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37767                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37768                         }
37769                         var markup = ct.apply(p);
37770                         if(!c.locked){
37771                             cb+= markup;
37772                         }else{
37773                             lcb+= markup;
37774                         }
37775                     }
37776                     var alt = [];
37777                     if(stripe && ((rowIndex+1) % 2 == 0)){
37778                         alt.push("x-grid-row-alt")
37779                     }
37780                     if(r.dirty){
37781                         alt.push(  " x-grid-dirty-row");
37782                     }
37783                     rp.cells = lcb;
37784                     if(this.getRowClass){
37785                         alt.push(this.getRowClass(r, rowIndex));
37786                     }
37787                     if (hasListener) {
37788                         rowcfg = {
37789                              
37790                             record: r,
37791                             rowIndex : rowIndex,
37792                             rowClass : ''
37793                         }
37794                         this.grid.fireEvent('rowclass', this, rowcfg);
37795                         alt.push(rowcfg.rowClass);
37796                     }
37797                     rp.alt = alt.join(" ");
37798                     lbuf+= rt.apply(rp);
37799                     rp.cells = cb;
37800                     buf+=  rt.apply(rp);
37801                 }
37802                 return [lbuf, buf];
37803             } :
37804             function(cs, rs, ds, startRow, colCount, stripe){
37805                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37806                 // buffers
37807                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37808                 var hasListener = this.grid.hasListener('rowclass');
37809  
37810                 var rowcfg = {};
37811                 for(var j = 0, len = rs.length; j < len; j++){
37812                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37813                     for(var i = 0; i < colCount; i++){
37814                         c = cs[i];
37815                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37816                         p.id = c.id;
37817                         p.css = p.attr = "";
37818                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37819                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37820                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37821                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37822                         }
37823                         
37824                         var markup = ct.apply(p);
37825                         if(!c.locked){
37826                             cb[cb.length] = markup;
37827                         }else{
37828                             lcb[lcb.length] = markup;
37829                         }
37830                     }
37831                     var alt = [];
37832                     if(stripe && ((rowIndex+1) % 2 == 0)){
37833                         alt.push( "x-grid-row-alt");
37834                     }
37835                     if(r.dirty){
37836                         alt.push(" x-grid-dirty-row");
37837                     }
37838                     rp.cells = lcb;
37839                     if(this.getRowClass){
37840                         alt.push( this.getRowClass(r, rowIndex));
37841                     }
37842                     if (hasListener) {
37843                         rowcfg = {
37844                              
37845                             record: r,
37846                             rowIndex : rowIndex,
37847                             rowClass : ''
37848                         }
37849                         this.grid.fireEvent('rowclass', this, rowcfg);
37850                         alt.push(rowcfg.rowClass);
37851                     }
37852                     rp.alt = alt.join(" ");
37853                     rp.cells = lcb.join("");
37854                     lbuf[lbuf.length] = rt.apply(rp);
37855                     rp.cells = cb.join("");
37856                     buf[buf.length] =  rt.apply(rp);
37857                 }
37858                 return [lbuf.join(""), buf.join("")];
37859             },
37860
37861     renderBody : function(){
37862         var markup = this.renderRows();
37863         var bt = this.templates.body;
37864         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37865     },
37866
37867     /**
37868      * Refreshes the grid
37869      * @param {Boolean} headersToo
37870      */
37871     refresh : function(headersToo){
37872         this.fireEvent("beforerefresh", this);
37873         this.grid.stopEditing();
37874         var result = this.renderBody();
37875         this.lockedBody.update(result[0]);
37876         this.mainBody.update(result[1]);
37877         if(headersToo === true){
37878             this.updateHeaders();
37879             this.updateColumns();
37880             this.updateSplitters();
37881             this.updateHeaderSortState();
37882         }
37883         this.syncRowHeights();
37884         this.layout();
37885         this.fireEvent("refresh", this);
37886     },
37887
37888     handleColumnMove : function(cm, oldIndex, newIndex){
37889         this.indexMap = null;
37890         var s = this.getScrollState();
37891         this.refresh(true);
37892         this.restoreScroll(s);
37893         this.afterMove(newIndex);
37894     },
37895
37896     afterMove : function(colIndex){
37897         if(this.enableMoveAnim && Roo.enableFx){
37898             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37899         }
37900         // if multisort - fix sortOrder, and reload..
37901         if (this.grid.dataSource.multiSort) {
37902             // the we can call sort again..
37903             var dm = this.grid.dataSource;
37904             var cm = this.grid.colModel;
37905             var so = [];
37906             for(var i = 0; i < cm.config.length; i++ ) {
37907                 
37908                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37909                     continue; // dont' bother, it's not in sort list or being set.
37910                 }
37911                 
37912                 so.push(cm.config[i].dataIndex);
37913             };
37914             dm.sortOrder = so;
37915             dm.load(dm.lastOptions);
37916             
37917             
37918         }
37919         
37920     },
37921
37922     updateCell : function(dm, rowIndex, dataIndex){
37923         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37924         if(typeof colIndex == "undefined"){ // not present in grid
37925             return;
37926         }
37927         var cm = this.grid.colModel;
37928         var cell = this.getCell(rowIndex, colIndex);
37929         var cellText = this.getCellText(rowIndex, colIndex);
37930
37931         var p = {
37932             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37933             id : cm.getColumnId(colIndex),
37934             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37935         };
37936         var renderer = cm.getRenderer(colIndex);
37937         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37938         if(typeof val == "undefined" || val === "") val = "&#160;";
37939         cellText.innerHTML = val;
37940         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37941         this.syncRowHeights(rowIndex, rowIndex);
37942     },
37943
37944     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37945         var maxWidth = 0;
37946         if(this.grid.autoSizeHeaders){
37947             var h = this.getHeaderCellMeasure(colIndex);
37948             maxWidth = Math.max(maxWidth, h.scrollWidth);
37949         }
37950         var tb, index;
37951         if(this.cm.isLocked(colIndex)){
37952             tb = this.getLockedTable();
37953             index = colIndex;
37954         }else{
37955             tb = this.getBodyTable();
37956             index = colIndex - this.cm.getLockedCount();
37957         }
37958         if(tb && tb.rows){
37959             var rows = tb.rows;
37960             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37961             for(var i = 0; i < stopIndex; i++){
37962                 var cell = rows[i].childNodes[index].firstChild;
37963                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37964             }
37965         }
37966         return maxWidth + /*margin for error in IE*/ 5;
37967     },
37968     /**
37969      * Autofit a column to its content.
37970      * @param {Number} colIndex
37971      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37972      */
37973      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37974          if(this.cm.isHidden(colIndex)){
37975              return; // can't calc a hidden column
37976          }
37977         if(forceMinSize){
37978             var cid = this.cm.getColumnId(colIndex);
37979             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37980            if(this.grid.autoSizeHeaders){
37981                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37982            }
37983         }
37984         var newWidth = this.calcColumnWidth(colIndex);
37985         this.cm.setColumnWidth(colIndex,
37986             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37987         if(!suppressEvent){
37988             this.grid.fireEvent("columnresize", colIndex, newWidth);
37989         }
37990     },
37991
37992     /**
37993      * Autofits all columns to their content and then expands to fit any extra space in the grid
37994      */
37995      autoSizeColumns : function(){
37996         var cm = this.grid.colModel;
37997         var colCount = cm.getColumnCount();
37998         for(var i = 0; i < colCount; i++){
37999             this.autoSizeColumn(i, true, true);
38000         }
38001         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38002             this.fitColumns();
38003         }else{
38004             this.updateColumns();
38005             this.layout();
38006         }
38007     },
38008
38009     /**
38010      * Autofits all columns to the grid's width proportionate with their current size
38011      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38012      */
38013     fitColumns : function(reserveScrollSpace){
38014         var cm = this.grid.colModel;
38015         var colCount = cm.getColumnCount();
38016         var cols = [];
38017         var width = 0;
38018         var i, w;
38019         for (i = 0; i < colCount; i++){
38020             if(!cm.isHidden(i) && !cm.isFixed(i)){
38021                 w = cm.getColumnWidth(i);
38022                 cols.push(i);
38023                 cols.push(w);
38024                 width += w;
38025             }
38026         }
38027         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38028         if(reserveScrollSpace){
38029             avail -= 17;
38030         }
38031         var frac = (avail - cm.getTotalWidth())/width;
38032         while (cols.length){
38033             w = cols.pop();
38034             i = cols.pop();
38035             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38036         }
38037         this.updateColumns();
38038         this.layout();
38039     },
38040
38041     onRowSelect : function(rowIndex){
38042         var row = this.getRowComposite(rowIndex);
38043         row.addClass("x-grid-row-selected");
38044     },
38045
38046     onRowDeselect : function(rowIndex){
38047         var row = this.getRowComposite(rowIndex);
38048         row.removeClass("x-grid-row-selected");
38049     },
38050
38051     onCellSelect : function(row, col){
38052         var cell = this.getCell(row, col);
38053         if(cell){
38054             Roo.fly(cell).addClass("x-grid-cell-selected");
38055         }
38056     },
38057
38058     onCellDeselect : function(row, col){
38059         var cell = this.getCell(row, col);
38060         if(cell){
38061             Roo.fly(cell).removeClass("x-grid-cell-selected");
38062         }
38063     },
38064
38065     updateHeaderSortState : function(){
38066         
38067         // sort state can be single { field: xxx, direction : yyy}
38068         // or   { xxx=>ASC , yyy : DESC ..... }
38069         
38070         var mstate = {};
38071         if (!this.ds.multiSort) { 
38072             var state = this.ds.getSortState();
38073             if(!state){
38074                 return;
38075             }
38076             mstate[state.field] = state.direction;
38077             // FIXME... - this is not used here.. but might be elsewhere..
38078             this.sortState = state;
38079             
38080         } else {
38081             mstate = this.ds.sortToggle;
38082         }
38083         //remove existing sort classes..
38084         
38085         var sc = this.sortClasses;
38086         var hds = this.el.select(this.headerSelector).removeClass(sc);
38087         
38088         for(var f in mstate) {
38089         
38090             var sortColumn = this.cm.findColumnIndex(f);
38091             
38092             if(sortColumn != -1){
38093                 var sortDir = mstate[f];        
38094                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38095             }
38096         }
38097         
38098          
38099         
38100     },
38101
38102
38103     handleHeaderClick : function(g, index,e){
38104         
38105         Roo.log("header click");
38106         
38107         if (Roo.isTouch) {
38108             // touch events on header are handled by context
38109             this.handleHdCtx(g,index,e);
38110             return;
38111         }
38112         
38113         
38114         if(this.headersDisabled){
38115             return;
38116         }
38117         var dm = g.dataSource, cm = g.colModel;
38118         if(!cm.isSortable(index)){
38119             return;
38120         }
38121         g.stopEditing();
38122         
38123         if (dm.multiSort) {
38124             // update the sortOrder
38125             var so = [];
38126             for(var i = 0; i < cm.config.length; i++ ) {
38127                 
38128                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38129                     continue; // dont' bother, it's not in sort list or being set.
38130                 }
38131                 
38132                 so.push(cm.config[i].dataIndex);
38133             };
38134             dm.sortOrder = so;
38135         }
38136         
38137         
38138         dm.sort(cm.getDataIndex(index));
38139     },
38140
38141
38142     destroy : function(){
38143         if(this.colMenu){
38144             this.colMenu.removeAll();
38145             Roo.menu.MenuMgr.unregister(this.colMenu);
38146             this.colMenu.getEl().remove();
38147             delete this.colMenu;
38148         }
38149         if(this.hmenu){
38150             this.hmenu.removeAll();
38151             Roo.menu.MenuMgr.unregister(this.hmenu);
38152             this.hmenu.getEl().remove();
38153             delete this.hmenu;
38154         }
38155         if(this.grid.enableColumnMove){
38156             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38157             if(dds){
38158                 for(var dd in dds){
38159                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38160                         var elid = dds[dd].dragElId;
38161                         dds[dd].unreg();
38162                         Roo.get(elid).remove();
38163                     } else if(dds[dd].config.isTarget){
38164                         dds[dd].proxyTop.remove();
38165                         dds[dd].proxyBottom.remove();
38166                         dds[dd].unreg();
38167                     }
38168                     if(Roo.dd.DDM.locationCache[dd]){
38169                         delete Roo.dd.DDM.locationCache[dd];
38170                     }
38171                 }
38172                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38173             }
38174         }
38175         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38176         this.bind(null, null);
38177         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38178     },
38179
38180     handleLockChange : function(){
38181         this.refresh(true);
38182     },
38183
38184     onDenyColumnLock : function(){
38185
38186     },
38187
38188     onDenyColumnHide : function(){
38189
38190     },
38191
38192     handleHdMenuClick : function(item){
38193         var index = this.hdCtxIndex;
38194         var cm = this.cm, ds = this.ds;
38195         switch(item.id){
38196             case "asc":
38197                 ds.sort(cm.getDataIndex(index), "ASC");
38198                 break;
38199             case "desc":
38200                 ds.sort(cm.getDataIndex(index), "DESC");
38201                 break;
38202             case "lock":
38203                 var lc = cm.getLockedCount();
38204                 if(cm.getColumnCount(true) <= lc+1){
38205                     this.onDenyColumnLock();
38206                     return;
38207                 }
38208                 if(lc != index){
38209                     cm.setLocked(index, true, true);
38210                     cm.moveColumn(index, lc);
38211                     this.grid.fireEvent("columnmove", index, lc);
38212                 }else{
38213                     cm.setLocked(index, true);
38214                 }
38215             break;
38216             case "unlock":
38217                 var lc = cm.getLockedCount();
38218                 if((lc-1) != index){
38219                     cm.setLocked(index, false, true);
38220                     cm.moveColumn(index, lc-1);
38221                     this.grid.fireEvent("columnmove", index, lc-1);
38222                 }else{
38223                     cm.setLocked(index, false);
38224                 }
38225             break;
38226             case 'wider': // used to expand cols on touch..
38227             case 'narrow':
38228                 var cw = cm.getColumnWidth(index);
38229                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38230                 cw = Math.max(0, cw);
38231                 cw = Math.min(cw,4000);
38232                 cm.setColumnWidth(index, cw);
38233                 break;
38234                 
38235             default:
38236                 index = cm.getIndexById(item.id.substr(4));
38237                 if(index != -1){
38238                     if(item.checked && cm.getColumnCount(true) <= 1){
38239                         this.onDenyColumnHide();
38240                         return false;
38241                     }
38242                     cm.setHidden(index, item.checked);
38243                 }
38244         }
38245         return true;
38246     },
38247
38248     beforeColMenuShow : function(){
38249         var cm = this.cm,  colCount = cm.getColumnCount();
38250         this.colMenu.removeAll();
38251         for(var i = 0; i < colCount; i++){
38252             this.colMenu.add(new Roo.menu.CheckItem({
38253                 id: "col-"+cm.getColumnId(i),
38254                 text: cm.getColumnHeader(i),
38255                 checked: !cm.isHidden(i),
38256                 hideOnClick:false
38257             }));
38258         }
38259     },
38260
38261     handleHdCtx : function(g, index, e){
38262         e.stopEvent();
38263         var hd = this.getHeaderCell(index);
38264         this.hdCtxIndex = index;
38265         var ms = this.hmenu.items, cm = this.cm;
38266         ms.get("asc").setDisabled(!cm.isSortable(index));
38267         ms.get("desc").setDisabled(!cm.isSortable(index));
38268         if(this.grid.enableColLock !== false){
38269             ms.get("lock").setDisabled(cm.isLocked(index));
38270             ms.get("unlock").setDisabled(!cm.isLocked(index));
38271         }
38272         this.hmenu.show(hd, "tl-bl");
38273     },
38274
38275     handleHdOver : function(e){
38276         var hd = this.findHeaderCell(e.getTarget());
38277         if(hd && !this.headersDisabled){
38278             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38279                this.fly(hd).addClass("x-grid-hd-over");
38280             }
38281         }
38282     },
38283
38284     handleHdOut : function(e){
38285         var hd = this.findHeaderCell(e.getTarget());
38286         if(hd){
38287             this.fly(hd).removeClass("x-grid-hd-over");
38288         }
38289     },
38290
38291     handleSplitDblClick : function(e, t){
38292         var i = this.getCellIndex(t);
38293         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38294             this.autoSizeColumn(i, true);
38295             this.layout();
38296         }
38297     },
38298
38299     render : function(){
38300
38301         var cm = this.cm;
38302         var colCount = cm.getColumnCount();
38303
38304         if(this.grid.monitorWindowResize === true){
38305             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38306         }
38307         var header = this.renderHeaders();
38308         var body = this.templates.body.apply({rows:""});
38309         var html = this.templates.master.apply({
38310             lockedBody: body,
38311             body: body,
38312             lockedHeader: header[0],
38313             header: header[1]
38314         });
38315
38316         //this.updateColumns();
38317
38318         this.grid.getGridEl().dom.innerHTML = html;
38319
38320         this.initElements();
38321         
38322         // a kludge to fix the random scolling effect in webkit
38323         this.el.on("scroll", function() {
38324             this.el.dom.scrollTop=0; // hopefully not recursive..
38325         },this);
38326
38327         this.scroller.on("scroll", this.handleScroll, this);
38328         this.lockedBody.on("mousewheel", this.handleWheel, this);
38329         this.mainBody.on("mousewheel", this.handleWheel, this);
38330
38331         this.mainHd.on("mouseover", this.handleHdOver, this);
38332         this.mainHd.on("mouseout", this.handleHdOut, this);
38333         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38334                 {delegate: "."+this.splitClass});
38335
38336         this.lockedHd.on("mouseover", this.handleHdOver, this);
38337         this.lockedHd.on("mouseout", this.handleHdOut, this);
38338         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38339                 {delegate: "."+this.splitClass});
38340
38341         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38342             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38343         }
38344
38345         this.updateSplitters();
38346
38347         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38348             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38349             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38350         }
38351
38352         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38353             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38354             this.hmenu.add(
38355                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38356                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38357             );
38358             if(this.grid.enableColLock !== false){
38359                 this.hmenu.add('-',
38360                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38361                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38362                 );
38363             }
38364             if (Roo.isTouch) {
38365                  this.hmenu.add('-',
38366                     {id:"wider", text: this.columnsWiderText},
38367                     {id:"narrow", text: this.columnsNarrowText }
38368                 );
38369                 
38370                  
38371             }
38372             
38373             if(this.grid.enableColumnHide !== false){
38374
38375                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38376                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38377                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38378
38379                 this.hmenu.add('-',
38380                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38381                 );
38382             }
38383             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38384
38385             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38386         }
38387
38388         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38389             this.dd = new Roo.grid.GridDragZone(this.grid, {
38390                 ddGroup : this.grid.ddGroup || 'GridDD'
38391             });
38392             
38393         }
38394
38395         /*
38396         for(var i = 0; i < colCount; i++){
38397             if(cm.isHidden(i)){
38398                 this.hideColumn(i);
38399             }
38400             if(cm.config[i].align){
38401                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38402                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38403             }
38404         }*/
38405         
38406         this.updateHeaderSortState();
38407
38408         this.beforeInitialResize();
38409         this.layout(true);
38410
38411         // two part rendering gives faster view to the user
38412         this.renderPhase2.defer(1, this);
38413     },
38414
38415     renderPhase2 : function(){
38416         // render the rows now
38417         this.refresh();
38418         if(this.grid.autoSizeColumns){
38419             this.autoSizeColumns();
38420         }
38421     },
38422
38423     beforeInitialResize : function(){
38424
38425     },
38426
38427     onColumnSplitterMoved : function(i, w){
38428         this.userResized = true;
38429         var cm = this.grid.colModel;
38430         cm.setColumnWidth(i, w, true);
38431         var cid = cm.getColumnId(i);
38432         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38433         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38434         this.updateSplitters();
38435         this.layout();
38436         this.grid.fireEvent("columnresize", i, w);
38437     },
38438
38439     syncRowHeights : function(startIndex, endIndex){
38440         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38441             startIndex = startIndex || 0;
38442             var mrows = this.getBodyTable().rows;
38443             var lrows = this.getLockedTable().rows;
38444             var len = mrows.length-1;
38445             endIndex = Math.min(endIndex || len, len);
38446             for(var i = startIndex; i <= endIndex; i++){
38447                 var m = mrows[i], l = lrows[i];
38448                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38449                 m.style.height = l.style.height = h + "px";
38450             }
38451         }
38452     },
38453
38454     layout : function(initialRender, is2ndPass){
38455         var g = this.grid;
38456         var auto = g.autoHeight;
38457         var scrollOffset = 16;
38458         var c = g.getGridEl(), cm = this.cm,
38459                 expandCol = g.autoExpandColumn,
38460                 gv = this;
38461         //c.beginMeasure();
38462
38463         if(!c.dom.offsetWidth){ // display:none?
38464             if(initialRender){
38465                 this.lockedWrap.show();
38466                 this.mainWrap.show();
38467             }
38468             return;
38469         }
38470
38471         var hasLock = this.cm.isLocked(0);
38472
38473         var tbh = this.headerPanel.getHeight();
38474         var bbh = this.footerPanel.getHeight();
38475
38476         if(auto){
38477             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38478             var newHeight = ch + c.getBorderWidth("tb");
38479             if(g.maxHeight){
38480                 newHeight = Math.min(g.maxHeight, newHeight);
38481             }
38482             c.setHeight(newHeight);
38483         }
38484
38485         if(g.autoWidth){
38486             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38487         }
38488
38489         var s = this.scroller;
38490
38491         var csize = c.getSize(true);
38492
38493         this.el.setSize(csize.width, csize.height);
38494
38495         this.headerPanel.setWidth(csize.width);
38496         this.footerPanel.setWidth(csize.width);
38497
38498         var hdHeight = this.mainHd.getHeight();
38499         var vw = csize.width;
38500         var vh = csize.height - (tbh + bbh);
38501
38502         s.setSize(vw, vh);
38503
38504         var bt = this.getBodyTable();
38505         var ltWidth = hasLock ?
38506                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38507
38508         var scrollHeight = bt.offsetHeight;
38509         var scrollWidth = ltWidth + bt.offsetWidth;
38510         var vscroll = false, hscroll = false;
38511
38512         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38513
38514         var lw = this.lockedWrap, mw = this.mainWrap;
38515         var lb = this.lockedBody, mb = this.mainBody;
38516
38517         setTimeout(function(){
38518             var t = s.dom.offsetTop;
38519             var w = s.dom.clientWidth,
38520                 h = s.dom.clientHeight;
38521
38522             lw.setTop(t);
38523             lw.setSize(ltWidth, h);
38524
38525             mw.setLeftTop(ltWidth, t);
38526             mw.setSize(w-ltWidth, h);
38527
38528             lb.setHeight(h-hdHeight);
38529             mb.setHeight(h-hdHeight);
38530
38531             if(is2ndPass !== true && !gv.userResized && expandCol){
38532                 // high speed resize without full column calculation
38533                 
38534                 var ci = cm.getIndexById(expandCol);
38535                 if (ci < 0) {
38536                     ci = cm.findColumnIndex(expandCol);
38537                 }
38538                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38539                 var expandId = cm.getColumnId(ci);
38540                 var  tw = cm.getTotalWidth(false);
38541                 var currentWidth = cm.getColumnWidth(ci);
38542                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38543                 if(currentWidth != cw){
38544                     cm.setColumnWidth(ci, cw, true);
38545                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38546                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38547                     gv.updateSplitters();
38548                     gv.layout(false, true);
38549                 }
38550             }
38551
38552             if(initialRender){
38553                 lw.show();
38554                 mw.show();
38555             }
38556             //c.endMeasure();
38557         }, 10);
38558     },
38559
38560     onWindowResize : function(){
38561         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38562             return;
38563         }
38564         this.layout();
38565     },
38566
38567     appendFooter : function(parentEl){
38568         return null;
38569     },
38570
38571     sortAscText : "Sort Ascending",
38572     sortDescText : "Sort Descending",
38573     lockText : "Lock Column",
38574     unlockText : "Unlock Column",
38575     columnsText : "Columns",
38576  
38577     columnsWiderText : "Wider",
38578     columnsNarrowText : "Thinner"
38579 });
38580
38581
38582 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38583     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38584     this.proxy.el.addClass('x-grid3-col-dd');
38585 };
38586
38587 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38588     handleMouseDown : function(e){
38589
38590     },
38591
38592     callHandleMouseDown : function(e){
38593         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38594     }
38595 });
38596 /*
38597  * Based on:
38598  * Ext JS Library 1.1.1
38599  * Copyright(c) 2006-2007, Ext JS, LLC.
38600  *
38601  * Originally Released Under LGPL - original licence link has changed is not relivant.
38602  *
38603  * Fork - LGPL
38604  * <script type="text/javascript">
38605  */
38606  
38607 // private
38608 // This is a support class used internally by the Grid components
38609 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38610     this.grid = grid;
38611     this.view = grid.getView();
38612     this.proxy = this.view.resizeProxy;
38613     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38614         "gridSplitters" + this.grid.getGridEl().id, {
38615         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38616     });
38617     this.setHandleElId(Roo.id(hd));
38618     this.setOuterHandleElId(Roo.id(hd2));
38619     this.scroll = false;
38620 };
38621 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38622     fly: Roo.Element.fly,
38623
38624     b4StartDrag : function(x, y){
38625         this.view.headersDisabled = true;
38626         this.proxy.setHeight(this.view.mainWrap.getHeight());
38627         var w = this.cm.getColumnWidth(this.cellIndex);
38628         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38629         this.resetConstraints();
38630         this.setXConstraint(minw, 1000);
38631         this.setYConstraint(0, 0);
38632         this.minX = x - minw;
38633         this.maxX = x + 1000;
38634         this.startPos = x;
38635         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38636     },
38637
38638
38639     handleMouseDown : function(e){
38640         ev = Roo.EventObject.setEvent(e);
38641         var t = this.fly(ev.getTarget());
38642         if(t.hasClass("x-grid-split")){
38643             this.cellIndex = this.view.getCellIndex(t.dom);
38644             this.split = t.dom;
38645             this.cm = this.grid.colModel;
38646             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38647                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38648             }
38649         }
38650     },
38651
38652     endDrag : function(e){
38653         this.view.headersDisabled = false;
38654         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38655         var diff = endX - this.startPos;
38656         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38657     },
38658
38659     autoOffset : function(){
38660         this.setDelta(0,0);
38661     }
38662 });/*
38663  * Based on:
38664  * Ext JS Library 1.1.1
38665  * Copyright(c) 2006-2007, Ext JS, LLC.
38666  *
38667  * Originally Released Under LGPL - original licence link has changed is not relivant.
38668  *
38669  * Fork - LGPL
38670  * <script type="text/javascript">
38671  */
38672  
38673 // private
38674 // This is a support class used internally by the Grid components
38675 Roo.grid.GridDragZone = function(grid, config){
38676     this.view = grid.getView();
38677     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38678     if(this.view.lockedBody){
38679         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38680         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38681     }
38682     this.scroll = false;
38683     this.grid = grid;
38684     this.ddel = document.createElement('div');
38685     this.ddel.className = 'x-grid-dd-wrap';
38686 };
38687
38688 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38689     ddGroup : "GridDD",
38690
38691     getDragData : function(e){
38692         var t = Roo.lib.Event.getTarget(e);
38693         var rowIndex = this.view.findRowIndex(t);
38694         var sm = this.grid.selModel;
38695             
38696         //Roo.log(rowIndex);
38697         
38698         if (sm.getSelectedCell) {
38699             // cell selection..
38700             if (!sm.getSelectedCell()) {
38701                 return false;
38702             }
38703             if (rowIndex != sm.getSelectedCell()[0]) {
38704                 return false;
38705             }
38706         
38707         }
38708         
38709         if(rowIndex !== false){
38710             
38711             // if editorgrid.. 
38712             
38713             
38714             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38715                
38716             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38717               //  
38718             //}
38719             if (e.hasModifier()){
38720                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38721             }
38722             
38723             Roo.log("getDragData");
38724             
38725             return {
38726                 grid: this.grid,
38727                 ddel: this.ddel,
38728                 rowIndex: rowIndex,
38729                 selections:sm.getSelections ? sm.getSelections() : (
38730                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38731                 )
38732             };
38733         }
38734         return false;
38735     },
38736
38737     onInitDrag : function(e){
38738         var data = this.dragData;
38739         this.ddel.innerHTML = this.grid.getDragDropText();
38740         this.proxy.update(this.ddel);
38741         // fire start drag?
38742     },
38743
38744     afterRepair : function(){
38745         this.dragging = false;
38746     },
38747
38748     getRepairXY : function(e, data){
38749         return false;
38750     },
38751
38752     onEndDrag : function(data, e){
38753         // fire end drag?
38754     },
38755
38756     onValidDrop : function(dd, e, id){
38757         // fire drag drop?
38758         this.hideProxy();
38759     },
38760
38761     beforeInvalidDrop : function(e, id){
38762
38763     }
38764 });/*
38765  * Based on:
38766  * Ext JS Library 1.1.1
38767  * Copyright(c) 2006-2007, Ext JS, LLC.
38768  *
38769  * Originally Released Under LGPL - original licence link has changed is not relivant.
38770  *
38771  * Fork - LGPL
38772  * <script type="text/javascript">
38773  */
38774  
38775
38776 /**
38777  * @class Roo.grid.ColumnModel
38778  * @extends Roo.util.Observable
38779  * This is the default implementation of a ColumnModel used by the Grid. It defines
38780  * the columns in the grid.
38781  * <br>Usage:<br>
38782  <pre><code>
38783  var colModel = new Roo.grid.ColumnModel([
38784         {header: "Ticker", width: 60, sortable: true, locked: true},
38785         {header: "Company Name", width: 150, sortable: true},
38786         {header: "Market Cap.", width: 100, sortable: true},
38787         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38788         {header: "Employees", width: 100, sortable: true, resizable: false}
38789  ]);
38790  </code></pre>
38791  * <p>
38792  
38793  * The config options listed for this class are options which may appear in each
38794  * individual column definition.
38795  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38796  * @constructor
38797  * @param {Object} config An Array of column config objects. See this class's
38798  * config objects for details.
38799 */
38800 Roo.grid.ColumnModel = function(config){
38801         /**
38802      * The config passed into the constructor
38803      */
38804     this.config = config;
38805     this.lookup = {};
38806
38807     // if no id, create one
38808     // if the column does not have a dataIndex mapping,
38809     // map it to the order it is in the config
38810     for(var i = 0, len = config.length; i < len; i++){
38811         var c = config[i];
38812         if(typeof c.dataIndex == "undefined"){
38813             c.dataIndex = i;
38814         }
38815         if(typeof c.renderer == "string"){
38816             c.renderer = Roo.util.Format[c.renderer];
38817         }
38818         if(typeof c.id == "undefined"){
38819             c.id = Roo.id();
38820         }
38821         if(c.editor && c.editor.xtype){
38822             c.editor  = Roo.factory(c.editor, Roo.grid);
38823         }
38824         if(c.editor && c.editor.isFormField){
38825             c.editor = new Roo.grid.GridEditor(c.editor);
38826         }
38827         this.lookup[c.id] = c;
38828     }
38829
38830     /**
38831      * The width of columns which have no width specified (defaults to 100)
38832      * @type Number
38833      */
38834     this.defaultWidth = 100;
38835
38836     /**
38837      * Default sortable of columns which have no sortable specified (defaults to false)
38838      * @type Boolean
38839      */
38840     this.defaultSortable = false;
38841
38842     this.addEvents({
38843         /**
38844              * @event widthchange
38845              * Fires when the width of a column changes.
38846              * @param {ColumnModel} this
38847              * @param {Number} columnIndex The column index
38848              * @param {Number} newWidth The new width
38849              */
38850             "widthchange": true,
38851         /**
38852              * @event headerchange
38853              * Fires when the text of a header changes.
38854              * @param {ColumnModel} this
38855              * @param {Number} columnIndex The column index
38856              * @param {Number} newText The new header text
38857              */
38858             "headerchange": true,
38859         /**
38860              * @event hiddenchange
38861              * Fires when a column is hidden or "unhidden".
38862              * @param {ColumnModel} this
38863              * @param {Number} columnIndex The column index
38864              * @param {Boolean} hidden true if hidden, false otherwise
38865              */
38866             "hiddenchange": true,
38867             /**
38868          * @event columnmoved
38869          * Fires when a column is moved.
38870          * @param {ColumnModel} this
38871          * @param {Number} oldIndex
38872          * @param {Number} newIndex
38873          */
38874         "columnmoved" : true,
38875         /**
38876          * @event columlockchange
38877          * Fires when a column's locked state is changed
38878          * @param {ColumnModel} this
38879          * @param {Number} colIndex
38880          * @param {Boolean} locked true if locked
38881          */
38882         "columnlockchange" : true
38883     });
38884     Roo.grid.ColumnModel.superclass.constructor.call(this);
38885 };
38886 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38887     /**
38888      * @cfg {String} header The header text to display in the Grid view.
38889      */
38890     /**
38891      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38892      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38893      * specified, the column's index is used as an index into the Record's data Array.
38894      */
38895     /**
38896      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38897      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38898      */
38899     /**
38900      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38901      * Defaults to the value of the {@link #defaultSortable} property.
38902      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38903      */
38904     /**
38905      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38906      */
38907     /**
38908      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38909      */
38910     /**
38911      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38912      */
38913     /**
38914      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38915      */
38916     /**
38917      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38918      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38919      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38920      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38921      */
38922        /**
38923      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38924      */
38925     /**
38926      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38927      */
38928     /**
38929      * @cfg {String} cursor (Optional)
38930      */
38931     /**
38932      * @cfg {String} tooltip (Optional)
38933      */
38934     /**
38935      * Returns the id of the column at the specified index.
38936      * @param {Number} index The column index
38937      * @return {String} the id
38938      */
38939     getColumnId : function(index){
38940         return this.config[index].id;
38941     },
38942
38943     /**
38944      * Returns the column for a specified id.
38945      * @param {String} id The column id
38946      * @return {Object} the column
38947      */
38948     getColumnById : function(id){
38949         return this.lookup[id];
38950     },
38951
38952     
38953     /**
38954      * Returns the column for a specified dataIndex.
38955      * @param {String} dataIndex The column dataIndex
38956      * @return {Object|Boolean} the column or false if not found
38957      */
38958     getColumnByDataIndex: function(dataIndex){
38959         var index = this.findColumnIndex(dataIndex);
38960         return index > -1 ? this.config[index] : false;
38961     },
38962     
38963     /**
38964      * Returns the index for a specified column id.
38965      * @param {String} id The column id
38966      * @return {Number} the index, or -1 if not found
38967      */
38968     getIndexById : function(id){
38969         for(var i = 0, len = this.config.length; i < len; i++){
38970             if(this.config[i].id == id){
38971                 return i;
38972             }
38973         }
38974         return -1;
38975     },
38976     
38977     /**
38978      * Returns the index for a specified column dataIndex.
38979      * @param {String} dataIndex The column dataIndex
38980      * @return {Number} the index, or -1 if not found
38981      */
38982     
38983     findColumnIndex : function(dataIndex){
38984         for(var i = 0, len = this.config.length; i < len; i++){
38985             if(this.config[i].dataIndex == dataIndex){
38986                 return i;
38987             }
38988         }
38989         return -1;
38990     },
38991     
38992     
38993     moveColumn : function(oldIndex, newIndex){
38994         var c = this.config[oldIndex];
38995         this.config.splice(oldIndex, 1);
38996         this.config.splice(newIndex, 0, c);
38997         this.dataMap = null;
38998         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38999     },
39000
39001     isLocked : function(colIndex){
39002         return this.config[colIndex].locked === true;
39003     },
39004
39005     setLocked : function(colIndex, value, suppressEvent){
39006         if(this.isLocked(colIndex) == value){
39007             return;
39008         }
39009         this.config[colIndex].locked = value;
39010         if(!suppressEvent){
39011             this.fireEvent("columnlockchange", this, colIndex, value);
39012         }
39013     },
39014
39015     getTotalLockedWidth : function(){
39016         var totalWidth = 0;
39017         for(var i = 0; i < this.config.length; i++){
39018             if(this.isLocked(i) && !this.isHidden(i)){
39019                 this.totalWidth += this.getColumnWidth(i);
39020             }
39021         }
39022         return totalWidth;
39023     },
39024
39025     getLockedCount : function(){
39026         for(var i = 0, len = this.config.length; i < len; i++){
39027             if(!this.isLocked(i)){
39028                 return i;
39029             }
39030         }
39031     },
39032
39033     /**
39034      * Returns the number of columns.
39035      * @return {Number}
39036      */
39037     getColumnCount : function(visibleOnly){
39038         if(visibleOnly === true){
39039             var c = 0;
39040             for(var i = 0, len = this.config.length; i < len; i++){
39041                 if(!this.isHidden(i)){
39042                     c++;
39043                 }
39044             }
39045             return c;
39046         }
39047         return this.config.length;
39048     },
39049
39050     /**
39051      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39052      * @param {Function} fn
39053      * @param {Object} scope (optional)
39054      * @return {Array} result
39055      */
39056     getColumnsBy : function(fn, scope){
39057         var r = [];
39058         for(var i = 0, len = this.config.length; i < len; i++){
39059             var c = this.config[i];
39060             if(fn.call(scope||this, c, i) === true){
39061                 r[r.length] = c;
39062             }
39063         }
39064         return r;
39065     },
39066
39067     /**
39068      * Returns true if the specified column is sortable.
39069      * @param {Number} col The column index
39070      * @return {Boolean}
39071      */
39072     isSortable : function(col){
39073         if(typeof this.config[col].sortable == "undefined"){
39074             return this.defaultSortable;
39075         }
39076         return this.config[col].sortable;
39077     },
39078
39079     /**
39080      * Returns the rendering (formatting) function defined for the column.
39081      * @param {Number} col The column index.
39082      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39083      */
39084     getRenderer : function(col){
39085         if(!this.config[col].renderer){
39086             return Roo.grid.ColumnModel.defaultRenderer;
39087         }
39088         return this.config[col].renderer;
39089     },
39090
39091     /**
39092      * Sets the rendering (formatting) function for a column.
39093      * @param {Number} col The column index
39094      * @param {Function} fn The function to use to process the cell's raw data
39095      * to return HTML markup for the grid view. The render function is called with
39096      * the following parameters:<ul>
39097      * <li>Data value.</li>
39098      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39099      * <li>css A CSS style string to apply to the table cell.</li>
39100      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39101      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39102      * <li>Row index</li>
39103      * <li>Column index</li>
39104      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39105      */
39106     setRenderer : function(col, fn){
39107         this.config[col].renderer = fn;
39108     },
39109
39110     /**
39111      * Returns the width for the specified column.
39112      * @param {Number} col The column index
39113      * @return {Number}
39114      */
39115     getColumnWidth : function(col){
39116         return this.config[col].width * 1 || this.defaultWidth;
39117     },
39118
39119     /**
39120      * Sets the width for a column.
39121      * @param {Number} col The column index
39122      * @param {Number} width The new width
39123      */
39124     setColumnWidth : function(col, width, suppressEvent){
39125         this.config[col].width = width;
39126         this.totalWidth = null;
39127         if(!suppressEvent){
39128              this.fireEvent("widthchange", this, col, width);
39129         }
39130     },
39131
39132     /**
39133      * Returns the total width of all columns.
39134      * @param {Boolean} includeHidden True to include hidden column widths
39135      * @return {Number}
39136      */
39137     getTotalWidth : function(includeHidden){
39138         if(!this.totalWidth){
39139             this.totalWidth = 0;
39140             for(var i = 0, len = this.config.length; i < len; i++){
39141                 if(includeHidden || !this.isHidden(i)){
39142                     this.totalWidth += this.getColumnWidth(i);
39143                 }
39144             }
39145         }
39146         return this.totalWidth;
39147     },
39148
39149     /**
39150      * Returns the header for the specified column.
39151      * @param {Number} col The column index
39152      * @return {String}
39153      */
39154     getColumnHeader : function(col){
39155         return this.config[col].header;
39156     },
39157
39158     /**
39159      * Sets the header for a column.
39160      * @param {Number} col The column index
39161      * @param {String} header The new header
39162      */
39163     setColumnHeader : function(col, header){
39164         this.config[col].header = header;
39165         this.fireEvent("headerchange", this, col, header);
39166     },
39167
39168     /**
39169      * Returns the tooltip for the specified column.
39170      * @param {Number} col The column index
39171      * @return {String}
39172      */
39173     getColumnTooltip : function(col){
39174             return this.config[col].tooltip;
39175     },
39176     /**
39177      * Sets the tooltip for a column.
39178      * @param {Number} col The column index
39179      * @param {String} tooltip The new tooltip
39180      */
39181     setColumnTooltip : function(col, tooltip){
39182             this.config[col].tooltip = tooltip;
39183     },
39184
39185     /**
39186      * Returns the dataIndex for the specified column.
39187      * @param {Number} col The column index
39188      * @return {Number}
39189      */
39190     getDataIndex : function(col){
39191         return this.config[col].dataIndex;
39192     },
39193
39194     /**
39195      * Sets the dataIndex for a column.
39196      * @param {Number} col The column index
39197      * @param {Number} dataIndex The new dataIndex
39198      */
39199     setDataIndex : function(col, dataIndex){
39200         this.config[col].dataIndex = dataIndex;
39201     },
39202
39203     
39204     
39205     /**
39206      * Returns true if the cell is editable.
39207      * @param {Number} colIndex The column index
39208      * @param {Number} rowIndex The row index
39209      * @return {Boolean}
39210      */
39211     isCellEditable : function(colIndex, rowIndex){
39212         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39213     },
39214
39215     /**
39216      * Returns the editor defined for the cell/column.
39217      * return false or null to disable editing.
39218      * @param {Number} colIndex The column index
39219      * @param {Number} rowIndex The row index
39220      * @return {Object}
39221      */
39222     getCellEditor : function(colIndex, rowIndex){
39223         return this.config[colIndex].editor;
39224     },
39225
39226     /**
39227      * Sets if a column is editable.
39228      * @param {Number} col The column index
39229      * @param {Boolean} editable True if the column is editable
39230      */
39231     setEditable : function(col, editable){
39232         this.config[col].editable = editable;
39233     },
39234
39235
39236     /**
39237      * Returns true if the column is hidden.
39238      * @param {Number} colIndex The column index
39239      * @return {Boolean}
39240      */
39241     isHidden : function(colIndex){
39242         return this.config[colIndex].hidden;
39243     },
39244
39245
39246     /**
39247      * Returns true if the column width cannot be changed
39248      */
39249     isFixed : function(colIndex){
39250         return this.config[colIndex].fixed;
39251     },
39252
39253     /**
39254      * Returns true if the column can be resized
39255      * @return {Boolean}
39256      */
39257     isResizable : function(colIndex){
39258         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39259     },
39260     /**
39261      * Sets if a column is hidden.
39262      * @param {Number} colIndex The column index
39263      * @param {Boolean} hidden True if the column is hidden
39264      */
39265     setHidden : function(colIndex, hidden){
39266         this.config[colIndex].hidden = hidden;
39267         this.totalWidth = null;
39268         this.fireEvent("hiddenchange", this, colIndex, hidden);
39269     },
39270
39271     /**
39272      * Sets the editor for a column.
39273      * @param {Number} col The column index
39274      * @param {Object} editor The editor object
39275      */
39276     setEditor : function(col, editor){
39277         this.config[col].editor = editor;
39278     }
39279 });
39280
39281 Roo.grid.ColumnModel.defaultRenderer = function(value){
39282         if(typeof value == "string" && value.length < 1){
39283             return "&#160;";
39284         }
39285         return value;
39286 };
39287
39288 // Alias for backwards compatibility
39289 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39290 /*
39291  * Based on:
39292  * Ext JS Library 1.1.1
39293  * Copyright(c) 2006-2007, Ext JS, LLC.
39294  *
39295  * Originally Released Under LGPL - original licence link has changed is not relivant.
39296  *
39297  * Fork - LGPL
39298  * <script type="text/javascript">
39299  */
39300
39301 /**
39302  * @class Roo.grid.AbstractSelectionModel
39303  * @extends Roo.util.Observable
39304  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39305  * implemented by descendant classes.  This class should not be directly instantiated.
39306  * @constructor
39307  */
39308 Roo.grid.AbstractSelectionModel = function(){
39309     this.locked = false;
39310     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39311 };
39312
39313 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39314     /** @ignore Called by the grid automatically. Do not call directly. */
39315     init : function(grid){
39316         this.grid = grid;
39317         this.initEvents();
39318     },
39319
39320     /**
39321      * Locks the selections.
39322      */
39323     lock : function(){
39324         this.locked = true;
39325     },
39326
39327     /**
39328      * Unlocks the selections.
39329      */
39330     unlock : function(){
39331         this.locked = false;
39332     },
39333
39334     /**
39335      * Returns true if the selections are locked.
39336      * @return {Boolean}
39337      */
39338     isLocked : function(){
39339         return this.locked;
39340     }
39341 });/*
39342  * Based on:
39343  * Ext JS Library 1.1.1
39344  * Copyright(c) 2006-2007, Ext JS, LLC.
39345  *
39346  * Originally Released Under LGPL - original licence link has changed is not relivant.
39347  *
39348  * Fork - LGPL
39349  * <script type="text/javascript">
39350  */
39351 /**
39352  * @extends Roo.grid.AbstractSelectionModel
39353  * @class Roo.grid.RowSelectionModel
39354  * The default SelectionModel used by {@link Roo.grid.Grid}.
39355  * It supports multiple selections and keyboard selection/navigation. 
39356  * @constructor
39357  * @param {Object} config
39358  */
39359 Roo.grid.RowSelectionModel = function(config){
39360     Roo.apply(this, config);
39361     this.selections = new Roo.util.MixedCollection(false, function(o){
39362         return o.id;
39363     });
39364
39365     this.last = false;
39366     this.lastActive = false;
39367
39368     this.addEvents({
39369         /**
39370              * @event selectionchange
39371              * Fires when the selection changes
39372              * @param {SelectionModel} this
39373              */
39374             "selectionchange" : true,
39375         /**
39376              * @event afterselectionchange
39377              * Fires after the selection changes (eg. by key press or clicking)
39378              * @param {SelectionModel} this
39379              */
39380             "afterselectionchange" : true,
39381         /**
39382              * @event beforerowselect
39383              * Fires when a row is selected being selected, return false to cancel.
39384              * @param {SelectionModel} this
39385              * @param {Number} rowIndex The selected index
39386              * @param {Boolean} keepExisting False if other selections will be cleared
39387              */
39388             "beforerowselect" : true,
39389         /**
39390              * @event rowselect
39391              * Fires when a row is selected.
39392              * @param {SelectionModel} this
39393              * @param {Number} rowIndex The selected index
39394              * @param {Roo.data.Record} r The record
39395              */
39396             "rowselect" : true,
39397         /**
39398              * @event rowdeselect
39399              * Fires when a row is deselected.
39400              * @param {SelectionModel} this
39401              * @param {Number} rowIndex The selected index
39402              */
39403         "rowdeselect" : true
39404     });
39405     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39406     this.locked = false;
39407 };
39408
39409 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39410     /**
39411      * @cfg {Boolean} singleSelect
39412      * True to allow selection of only one row at a time (defaults to false)
39413      */
39414     singleSelect : false,
39415
39416     // private
39417     initEvents : function(){
39418
39419         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39420             this.grid.on("mousedown", this.handleMouseDown, this);
39421         }else{ // allow click to work like normal
39422             this.grid.on("rowclick", this.handleDragableRowClick, this);
39423         }
39424
39425         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39426             "up" : function(e){
39427                 if(!e.shiftKey){
39428                     this.selectPrevious(e.shiftKey);
39429                 }else if(this.last !== false && this.lastActive !== false){
39430                     var last = this.last;
39431                     this.selectRange(this.last,  this.lastActive-1);
39432                     this.grid.getView().focusRow(this.lastActive);
39433                     if(last !== false){
39434                         this.last = last;
39435                     }
39436                 }else{
39437                     this.selectFirstRow();
39438                 }
39439                 this.fireEvent("afterselectionchange", this);
39440             },
39441             "down" : function(e){
39442                 if(!e.shiftKey){
39443                     this.selectNext(e.shiftKey);
39444                 }else if(this.last !== false && this.lastActive !== false){
39445                     var last = this.last;
39446                     this.selectRange(this.last,  this.lastActive+1);
39447                     this.grid.getView().focusRow(this.lastActive);
39448                     if(last !== false){
39449                         this.last = last;
39450                     }
39451                 }else{
39452                     this.selectFirstRow();
39453                 }
39454                 this.fireEvent("afterselectionchange", this);
39455             },
39456             scope: this
39457         });
39458
39459         var view = this.grid.view;
39460         view.on("refresh", this.onRefresh, this);
39461         view.on("rowupdated", this.onRowUpdated, this);
39462         view.on("rowremoved", this.onRemove, this);
39463     },
39464
39465     // private
39466     onRefresh : function(){
39467         var ds = this.grid.dataSource, i, v = this.grid.view;
39468         var s = this.selections;
39469         s.each(function(r){
39470             if((i = ds.indexOfId(r.id)) != -1){
39471                 v.onRowSelect(i);
39472             }else{
39473                 s.remove(r);
39474             }
39475         });
39476     },
39477
39478     // private
39479     onRemove : function(v, index, r){
39480         this.selections.remove(r);
39481     },
39482
39483     // private
39484     onRowUpdated : function(v, index, r){
39485         if(this.isSelected(r)){
39486             v.onRowSelect(index);
39487         }
39488     },
39489
39490     /**
39491      * Select records.
39492      * @param {Array} records The records to select
39493      * @param {Boolean} keepExisting (optional) True to keep existing selections
39494      */
39495     selectRecords : function(records, keepExisting){
39496         if(!keepExisting){
39497             this.clearSelections();
39498         }
39499         var ds = this.grid.dataSource;
39500         for(var i = 0, len = records.length; i < len; i++){
39501             this.selectRow(ds.indexOf(records[i]), true);
39502         }
39503     },
39504
39505     /**
39506      * Gets the number of selected rows.
39507      * @return {Number}
39508      */
39509     getCount : function(){
39510         return this.selections.length;
39511     },
39512
39513     /**
39514      * Selects the first row in the grid.
39515      */
39516     selectFirstRow : function(){
39517         this.selectRow(0);
39518     },
39519
39520     /**
39521      * Select the last row.
39522      * @param {Boolean} keepExisting (optional) True to keep existing selections
39523      */
39524     selectLastRow : function(keepExisting){
39525         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39526     },
39527
39528     /**
39529      * Selects the row immediately following the last selected row.
39530      * @param {Boolean} keepExisting (optional) True to keep existing selections
39531      */
39532     selectNext : function(keepExisting){
39533         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39534             this.selectRow(this.last+1, keepExisting);
39535             this.grid.getView().focusRow(this.last);
39536         }
39537     },
39538
39539     /**
39540      * Selects the row that precedes the last selected row.
39541      * @param {Boolean} keepExisting (optional) True to keep existing selections
39542      */
39543     selectPrevious : function(keepExisting){
39544         if(this.last){
39545             this.selectRow(this.last-1, keepExisting);
39546             this.grid.getView().focusRow(this.last);
39547         }
39548     },
39549
39550     /**
39551      * Returns the selected records
39552      * @return {Array} Array of selected records
39553      */
39554     getSelections : function(){
39555         return [].concat(this.selections.items);
39556     },
39557
39558     /**
39559      * Returns the first selected record.
39560      * @return {Record}
39561      */
39562     getSelected : function(){
39563         return this.selections.itemAt(0);
39564     },
39565
39566
39567     /**
39568      * Clears all selections.
39569      */
39570     clearSelections : function(fast){
39571         if(this.locked) return;
39572         if(fast !== true){
39573             var ds = this.grid.dataSource;
39574             var s = this.selections;
39575             s.each(function(r){
39576                 this.deselectRow(ds.indexOfId(r.id));
39577             }, this);
39578             s.clear();
39579         }else{
39580             this.selections.clear();
39581         }
39582         this.last = false;
39583     },
39584
39585
39586     /**
39587      * Selects all rows.
39588      */
39589     selectAll : function(){
39590         if(this.locked) return;
39591         this.selections.clear();
39592         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39593             this.selectRow(i, true);
39594         }
39595     },
39596
39597     /**
39598      * Returns True if there is a selection.
39599      * @return {Boolean}
39600      */
39601     hasSelection : function(){
39602         return this.selections.length > 0;
39603     },
39604
39605     /**
39606      * Returns True if the specified row is selected.
39607      * @param {Number/Record} record The record or index of the record to check
39608      * @return {Boolean}
39609      */
39610     isSelected : function(index){
39611         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39612         return (r && this.selections.key(r.id) ? true : false);
39613     },
39614
39615     /**
39616      * Returns True if the specified record id is selected.
39617      * @param {String} id The id of record to check
39618      * @return {Boolean}
39619      */
39620     isIdSelected : function(id){
39621         return (this.selections.key(id) ? true : false);
39622     },
39623
39624     // private
39625     handleMouseDown : function(e, t){
39626         var view = this.grid.getView(), rowIndex;
39627         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39628             return;
39629         };
39630         if(e.shiftKey && this.last !== false){
39631             var last = this.last;
39632             this.selectRange(last, rowIndex, e.ctrlKey);
39633             this.last = last; // reset the last
39634             view.focusRow(rowIndex);
39635         }else{
39636             var isSelected = this.isSelected(rowIndex);
39637             if(e.button !== 0 && isSelected){
39638                 view.focusRow(rowIndex);
39639             }else if(e.ctrlKey && isSelected){
39640                 this.deselectRow(rowIndex);
39641             }else if(!isSelected){
39642                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39643                 view.focusRow(rowIndex);
39644             }
39645         }
39646         this.fireEvent("afterselectionchange", this);
39647     },
39648     // private
39649     handleDragableRowClick :  function(grid, rowIndex, e) 
39650     {
39651         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39652             this.selectRow(rowIndex, false);
39653             grid.view.focusRow(rowIndex);
39654              this.fireEvent("afterselectionchange", this);
39655         }
39656     },
39657     
39658     /**
39659      * Selects multiple rows.
39660      * @param {Array} rows Array of the indexes of the row to select
39661      * @param {Boolean} keepExisting (optional) True to keep existing selections
39662      */
39663     selectRows : function(rows, keepExisting){
39664         if(!keepExisting){
39665             this.clearSelections();
39666         }
39667         for(var i = 0, len = rows.length; i < len; i++){
39668             this.selectRow(rows[i], true);
39669         }
39670     },
39671
39672     /**
39673      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39674      * @param {Number} startRow The index of the first row in the range
39675      * @param {Number} endRow The index of the last row in the range
39676      * @param {Boolean} keepExisting (optional) True to retain existing selections
39677      */
39678     selectRange : function(startRow, endRow, keepExisting){
39679         if(this.locked) return;
39680         if(!keepExisting){
39681             this.clearSelections();
39682         }
39683         if(startRow <= endRow){
39684             for(var i = startRow; i <= endRow; i++){
39685                 this.selectRow(i, true);
39686             }
39687         }else{
39688             for(var i = startRow; i >= endRow; i--){
39689                 this.selectRow(i, true);
39690             }
39691         }
39692     },
39693
39694     /**
39695      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39696      * @param {Number} startRow The index of the first row in the range
39697      * @param {Number} endRow The index of the last row in the range
39698      */
39699     deselectRange : function(startRow, endRow, preventViewNotify){
39700         if(this.locked) return;
39701         for(var i = startRow; i <= endRow; i++){
39702             this.deselectRow(i, preventViewNotify);
39703         }
39704     },
39705
39706     /**
39707      * Selects a row.
39708      * @param {Number} row The index of the row to select
39709      * @param {Boolean} keepExisting (optional) True to keep existing selections
39710      */
39711     selectRow : function(index, keepExisting, preventViewNotify){
39712         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39713         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39714             if(!keepExisting || this.singleSelect){
39715                 this.clearSelections();
39716             }
39717             var r = this.grid.dataSource.getAt(index);
39718             this.selections.add(r);
39719             this.last = this.lastActive = index;
39720             if(!preventViewNotify){
39721                 this.grid.getView().onRowSelect(index);
39722             }
39723             this.fireEvent("rowselect", this, index, r);
39724             this.fireEvent("selectionchange", this);
39725         }
39726     },
39727
39728     /**
39729      * Deselects a row.
39730      * @param {Number} row The index of the row to deselect
39731      */
39732     deselectRow : function(index, preventViewNotify){
39733         if(this.locked) return;
39734         if(this.last == index){
39735             this.last = false;
39736         }
39737         if(this.lastActive == index){
39738             this.lastActive = false;
39739         }
39740         var r = this.grid.dataSource.getAt(index);
39741         this.selections.remove(r);
39742         if(!preventViewNotify){
39743             this.grid.getView().onRowDeselect(index);
39744         }
39745         this.fireEvent("rowdeselect", this, index);
39746         this.fireEvent("selectionchange", this);
39747     },
39748
39749     // private
39750     restoreLast : function(){
39751         if(this._last){
39752             this.last = this._last;
39753         }
39754     },
39755
39756     // private
39757     acceptsNav : function(row, col, cm){
39758         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39759     },
39760
39761     // private
39762     onEditorKey : function(field, e){
39763         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39764         if(k == e.TAB){
39765             e.stopEvent();
39766             ed.completeEdit();
39767             if(e.shiftKey){
39768                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39769             }else{
39770                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39771             }
39772         }else if(k == e.ENTER && !e.ctrlKey){
39773             e.stopEvent();
39774             ed.completeEdit();
39775             if(e.shiftKey){
39776                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39777             }else{
39778                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39779             }
39780         }else if(k == e.ESC){
39781             ed.cancelEdit();
39782         }
39783         if(newCell){
39784             g.startEditing(newCell[0], newCell[1]);
39785         }
39786     }
39787 });/*
39788  * Based on:
39789  * Ext JS Library 1.1.1
39790  * Copyright(c) 2006-2007, Ext JS, LLC.
39791  *
39792  * Originally Released Under LGPL - original licence link has changed is not relivant.
39793  *
39794  * Fork - LGPL
39795  * <script type="text/javascript">
39796  */
39797 /**
39798  * @class Roo.grid.CellSelectionModel
39799  * @extends Roo.grid.AbstractSelectionModel
39800  * This class provides the basic implementation for cell selection in a grid.
39801  * @constructor
39802  * @param {Object} config The object containing the configuration of this model.
39803  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39804  */
39805 Roo.grid.CellSelectionModel = function(config){
39806     Roo.apply(this, config);
39807
39808     this.selection = null;
39809
39810     this.addEvents({
39811         /**
39812              * @event beforerowselect
39813              * Fires before a cell is selected.
39814              * @param {SelectionModel} this
39815              * @param {Number} rowIndex The selected row index
39816              * @param {Number} colIndex The selected cell index
39817              */
39818             "beforecellselect" : true,
39819         /**
39820              * @event cellselect
39821              * Fires when a cell is selected.
39822              * @param {SelectionModel} this
39823              * @param {Number} rowIndex The selected row index
39824              * @param {Number} colIndex The selected cell index
39825              */
39826             "cellselect" : true,
39827         /**
39828              * @event selectionchange
39829              * Fires when the active selection changes.
39830              * @param {SelectionModel} this
39831              * @param {Object} selection null for no selection or an object (o) with two properties
39832                 <ul>
39833                 <li>o.record: the record object for the row the selection is in</li>
39834                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39835                 </ul>
39836              */
39837             "selectionchange" : true,
39838         /**
39839              * @event tabend
39840              * Fires when the tab (or enter) was pressed on the last editable cell
39841              * You can use this to trigger add new row.
39842              * @param {SelectionModel} this
39843              */
39844             "tabend" : true,
39845          /**
39846              * @event beforeeditnext
39847              * Fires before the next editable sell is made active
39848              * You can use this to skip to another cell or fire the tabend
39849              *    if you set cell to false
39850              * @param {Object} eventdata object : { cell : [ row, col ] } 
39851              */
39852             "beforeeditnext" : true
39853     });
39854     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39855 };
39856
39857 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39858     
39859     enter_is_tab: false,
39860
39861     /** @ignore */
39862     initEvents : function(){
39863         this.grid.on("mousedown", this.handleMouseDown, this);
39864         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39865         var view = this.grid.view;
39866         view.on("refresh", this.onViewChange, this);
39867         view.on("rowupdated", this.onRowUpdated, this);
39868         view.on("beforerowremoved", this.clearSelections, this);
39869         view.on("beforerowsinserted", this.clearSelections, this);
39870         if(this.grid.isEditor){
39871             this.grid.on("beforeedit", this.beforeEdit,  this);
39872         }
39873     },
39874
39875         //private
39876     beforeEdit : function(e){
39877         this.select(e.row, e.column, false, true, e.record);
39878     },
39879
39880         //private
39881     onRowUpdated : function(v, index, r){
39882         if(this.selection && this.selection.record == r){
39883             v.onCellSelect(index, this.selection.cell[1]);
39884         }
39885     },
39886
39887         //private
39888     onViewChange : function(){
39889         this.clearSelections(true);
39890     },
39891
39892         /**
39893          * Returns the currently selected cell,.
39894          * @return {Array} The selected cell (row, column) or null if none selected.
39895          */
39896     getSelectedCell : function(){
39897         return this.selection ? this.selection.cell : null;
39898     },
39899
39900     /**
39901      * Clears all selections.
39902      * @param {Boolean} true to prevent the gridview from being notified about the change.
39903      */
39904     clearSelections : function(preventNotify){
39905         var s = this.selection;
39906         if(s){
39907             if(preventNotify !== true){
39908                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39909             }
39910             this.selection = null;
39911             this.fireEvent("selectionchange", this, null);
39912         }
39913     },
39914
39915     /**
39916      * Returns true if there is a selection.
39917      * @return {Boolean}
39918      */
39919     hasSelection : function(){
39920         return this.selection ? true : false;
39921     },
39922
39923     /** @ignore */
39924     handleMouseDown : function(e, t){
39925         var v = this.grid.getView();
39926         if(this.isLocked()){
39927             return;
39928         };
39929         var row = v.findRowIndex(t);
39930         var cell = v.findCellIndex(t);
39931         if(row !== false && cell !== false){
39932             this.select(row, cell);
39933         }
39934     },
39935
39936     /**
39937      * Selects a cell.
39938      * @param {Number} rowIndex
39939      * @param {Number} collIndex
39940      */
39941     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39942         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39943             this.clearSelections();
39944             r = r || this.grid.dataSource.getAt(rowIndex);
39945             this.selection = {
39946                 record : r,
39947                 cell : [rowIndex, colIndex]
39948             };
39949             if(!preventViewNotify){
39950                 var v = this.grid.getView();
39951                 v.onCellSelect(rowIndex, colIndex);
39952                 if(preventFocus !== true){
39953                     v.focusCell(rowIndex, colIndex);
39954                 }
39955             }
39956             this.fireEvent("cellselect", this, rowIndex, colIndex);
39957             this.fireEvent("selectionchange", this, this.selection);
39958         }
39959     },
39960
39961         //private
39962     isSelectable : function(rowIndex, colIndex, cm){
39963         return !cm.isHidden(colIndex);
39964     },
39965
39966     /** @ignore */
39967     handleKeyDown : function(e){
39968         //Roo.log('Cell Sel Model handleKeyDown');
39969         if(!e.isNavKeyPress()){
39970             return;
39971         }
39972         var g = this.grid, s = this.selection;
39973         if(!s){
39974             e.stopEvent();
39975             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39976             if(cell){
39977                 this.select(cell[0], cell[1]);
39978             }
39979             return;
39980         }
39981         var sm = this;
39982         var walk = function(row, col, step){
39983             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39984         };
39985         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39986         var newCell;
39987
39988       
39989
39990         switch(k){
39991             case e.TAB:
39992                 // handled by onEditorKey
39993                 if (g.isEditor && g.editing) {
39994                     return;
39995                 }
39996                 if(e.shiftKey) {
39997                     newCell = walk(r, c-1, -1);
39998                 } else {
39999                     newCell = walk(r, c+1, 1);
40000                 }
40001                 break;
40002             
40003             case e.DOWN:
40004                newCell = walk(r+1, c, 1);
40005                 break;
40006             
40007             case e.UP:
40008                 newCell = walk(r-1, c, -1);
40009                 break;
40010             
40011             case e.RIGHT:
40012                 newCell = walk(r, c+1, 1);
40013                 break;
40014             
40015             case e.LEFT:
40016                 newCell = walk(r, c-1, -1);
40017                 break;
40018             
40019             case e.ENTER:
40020                 
40021                 if(g.isEditor && !g.editing){
40022                    g.startEditing(r, c);
40023                    e.stopEvent();
40024                    return;
40025                 }
40026                 
40027                 
40028              break;
40029         };
40030         if(newCell){
40031             this.select(newCell[0], newCell[1]);
40032             e.stopEvent();
40033             
40034         }
40035     },
40036
40037     acceptsNav : function(row, col, cm){
40038         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40039     },
40040     /**
40041      * Selects a cell.
40042      * @param {Number} field (not used) - as it's normally used as a listener
40043      * @param {Number} e - event - fake it by using
40044      *
40045      * var e = Roo.EventObjectImpl.prototype;
40046      * e.keyCode = e.TAB
40047      *
40048      * 
40049      */
40050     onEditorKey : function(field, e){
40051         
40052         var k = e.getKey(),
40053             newCell,
40054             g = this.grid,
40055             ed = g.activeEditor,
40056             forward = false;
40057         ///Roo.log('onEditorKey' + k);
40058         
40059         
40060         if (this.enter_is_tab && k == e.ENTER) {
40061             k = e.TAB;
40062         }
40063         
40064         if(k == e.TAB){
40065             if(e.shiftKey){
40066                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40067             }else{
40068                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40069                 forward = true;
40070             }
40071             
40072             e.stopEvent();
40073             
40074         } else if(k == e.ENTER &&  !e.ctrlKey){
40075             ed.completeEdit();
40076             e.stopEvent();
40077             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40078         
40079                 } else if(k == e.ESC){
40080             ed.cancelEdit();
40081         }
40082                 
40083         if (newCell) {
40084             var ecall = { cell : newCell, forward : forward };
40085             this.fireEvent('beforeeditnext', ecall );
40086             newCell = ecall.cell;
40087                         forward = ecall.forward;
40088         }
40089                 
40090         if(newCell){
40091             //Roo.log('next cell after edit');
40092             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40093         } else if (forward) {
40094             // tabbed past last
40095             this.fireEvent.defer(100, this, ['tabend',this]);
40096         }
40097     }
40098 });/*
40099  * Based on:
40100  * Ext JS Library 1.1.1
40101  * Copyright(c) 2006-2007, Ext JS, LLC.
40102  *
40103  * Originally Released Under LGPL - original licence link has changed is not relivant.
40104  *
40105  * Fork - LGPL
40106  * <script type="text/javascript">
40107  */
40108  
40109 /**
40110  * @class Roo.grid.EditorGrid
40111  * @extends Roo.grid.Grid
40112  * Class for creating and editable grid.
40113  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40114  * The container MUST have some type of size defined for the grid to fill. The container will be 
40115  * automatically set to position relative if it isn't already.
40116  * @param {Object} dataSource The data model to bind to
40117  * @param {Object} colModel The column model with info about this grid's columns
40118  */
40119 Roo.grid.EditorGrid = function(container, config){
40120     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40121     this.getGridEl().addClass("xedit-grid");
40122
40123     if(!this.selModel){
40124         this.selModel = new Roo.grid.CellSelectionModel();
40125     }
40126
40127     this.activeEditor = null;
40128
40129         this.addEvents({
40130             /**
40131              * @event beforeedit
40132              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40133              * <ul style="padding:5px;padding-left:16px;">
40134              * <li>grid - This grid</li>
40135              * <li>record - The record being edited</li>
40136              * <li>field - The field name being edited</li>
40137              * <li>value - The value for the field being edited.</li>
40138              * <li>row - The grid row index</li>
40139              * <li>column - The grid column index</li>
40140              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40141              * </ul>
40142              * @param {Object} e An edit event (see above for description)
40143              */
40144             "beforeedit" : true,
40145             /**
40146              * @event afteredit
40147              * Fires after a cell is edited. <br />
40148              * <ul style="padding:5px;padding-left:16px;">
40149              * <li>grid - This grid</li>
40150              * <li>record - The record being edited</li>
40151              * <li>field - The field name being edited</li>
40152              * <li>value - The value being set</li>
40153              * <li>originalValue - The original value for the field, before the edit.</li>
40154              * <li>row - The grid row index</li>
40155              * <li>column - The grid column index</li>
40156              * </ul>
40157              * @param {Object} e An edit event (see above for description)
40158              */
40159             "afteredit" : true,
40160             /**
40161              * @event validateedit
40162              * Fires after a cell is edited, but before the value is set in the record. 
40163          * You can use this to modify the value being set in the field, Return false
40164              * to cancel the change. The edit event object has the following properties <br />
40165              * <ul style="padding:5px;padding-left:16px;">
40166          * <li>editor - This editor</li>
40167              * <li>grid - This grid</li>
40168              * <li>record - The record being edited</li>
40169              * <li>field - The field name being edited</li>
40170              * <li>value - The value being set</li>
40171              * <li>originalValue - The original value for the field, before the edit.</li>
40172              * <li>row - The grid row index</li>
40173              * <li>column - The grid column index</li>
40174              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40175              * </ul>
40176              * @param {Object} e An edit event (see above for description)
40177              */
40178             "validateedit" : true
40179         });
40180     this.on("bodyscroll", this.stopEditing,  this);
40181     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40182 };
40183
40184 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40185     /**
40186      * @cfg {Number} clicksToEdit
40187      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40188      */
40189     clicksToEdit: 2,
40190
40191     // private
40192     isEditor : true,
40193     // private
40194     trackMouseOver: false, // causes very odd FF errors
40195
40196     onCellDblClick : function(g, row, col){
40197         this.startEditing(row, col);
40198     },
40199
40200     onEditComplete : function(ed, value, startValue){
40201         this.editing = false;
40202         this.activeEditor = null;
40203         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40204         var r = ed.record;
40205         var field = this.colModel.getDataIndex(ed.col);
40206         var e = {
40207             grid: this,
40208             record: r,
40209             field: field,
40210             originalValue: startValue,
40211             value: value,
40212             row: ed.row,
40213             column: ed.col,
40214             cancel:false,
40215             editor: ed
40216         };
40217         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40218         cell.show();
40219           
40220         if(String(value) !== String(startValue)){
40221             
40222             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40223                 r.set(field, e.value);
40224                 // if we are dealing with a combo box..
40225                 // then we also set the 'name' colum to be the displayField
40226                 if (ed.field.displayField && ed.field.name) {
40227                     r.set(ed.field.name, ed.field.el.dom.value);
40228                 }
40229                 
40230                 delete e.cancel; //?? why!!!
40231                 this.fireEvent("afteredit", e);
40232             }
40233         } else {
40234             this.fireEvent("afteredit", e); // always fire it!
40235         }
40236         this.view.focusCell(ed.row, ed.col);
40237     },
40238
40239     /**
40240      * Starts editing the specified for the specified row/column
40241      * @param {Number} rowIndex
40242      * @param {Number} colIndex
40243      */
40244     startEditing : function(row, col){
40245         this.stopEditing();
40246         if(this.colModel.isCellEditable(col, row)){
40247             this.view.ensureVisible(row, col, true);
40248           
40249             var r = this.dataSource.getAt(row);
40250             var field = this.colModel.getDataIndex(col);
40251             var cell = Roo.get(this.view.getCell(row,col));
40252             var e = {
40253                 grid: this,
40254                 record: r,
40255                 field: field,
40256                 value: r.data[field],
40257                 row: row,
40258                 column: col,
40259                 cancel:false 
40260             };
40261             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40262                 this.editing = true;
40263                 var ed = this.colModel.getCellEditor(col, row);
40264                 
40265                 if (!ed) {
40266                     return;
40267                 }
40268                 if(!ed.rendered){
40269                     ed.render(ed.parentEl || document.body);
40270                 }
40271                 ed.field.reset();
40272                
40273                 cell.hide();
40274                 
40275                 (function(){ // complex but required for focus issues in safari, ie and opera
40276                     ed.row = row;
40277                     ed.col = col;
40278                     ed.record = r;
40279                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40280                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40281                     this.activeEditor = ed;
40282                     var v = r.data[field];
40283                     ed.startEdit(this.view.getCell(row, col), v);
40284                     // combo's with 'displayField and name set
40285                     if (ed.field.displayField && ed.field.name) {
40286                         ed.field.el.dom.value = r.data[ed.field.name];
40287                     }
40288                     
40289                     
40290                 }).defer(50, this);
40291             }
40292         }
40293     },
40294         
40295     /**
40296      * Stops any active editing
40297      */
40298     stopEditing : function(){
40299         if(this.activeEditor){
40300             this.activeEditor.completeEdit();
40301         }
40302         this.activeEditor = null;
40303     },
40304         
40305          /**
40306      * Called to get grid's drag proxy text, by default returns this.ddText.
40307      * @return {String}
40308      */
40309     getDragDropText : function(){
40310         var count = this.selModel.getSelectedCell() ? 1 : 0;
40311         return String.format(this.ddText, count, count == 1 ? '' : 's');
40312     }
40313         
40314 });/*
40315  * Based on:
40316  * Ext JS Library 1.1.1
40317  * Copyright(c) 2006-2007, Ext JS, LLC.
40318  *
40319  * Originally Released Under LGPL - original licence link has changed is not relivant.
40320  *
40321  * Fork - LGPL
40322  * <script type="text/javascript">
40323  */
40324
40325 // private - not really -- you end up using it !
40326 // This is a support class used internally by the Grid components
40327
40328 /**
40329  * @class Roo.grid.GridEditor
40330  * @extends Roo.Editor
40331  * Class for creating and editable grid elements.
40332  * @param {Object} config any settings (must include field)
40333  */
40334 Roo.grid.GridEditor = function(field, config){
40335     if (!config && field.field) {
40336         config = field;
40337         field = Roo.factory(config.field, Roo.form);
40338     }
40339     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40340     field.monitorTab = false;
40341 };
40342
40343 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40344     
40345     /**
40346      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40347      */
40348     
40349     alignment: "tl-tl",
40350     autoSize: "width",
40351     hideEl : false,
40352     cls: "x-small-editor x-grid-editor",
40353     shim:false,
40354     shadow:"frame"
40355 });/*
40356  * Based on:
40357  * Ext JS Library 1.1.1
40358  * Copyright(c) 2006-2007, Ext JS, LLC.
40359  *
40360  * Originally Released Under LGPL - original licence link has changed is not relivant.
40361  *
40362  * Fork - LGPL
40363  * <script type="text/javascript">
40364  */
40365   
40366
40367   
40368 Roo.grid.PropertyRecord = Roo.data.Record.create([
40369     {name:'name',type:'string'},  'value'
40370 ]);
40371
40372
40373 Roo.grid.PropertyStore = function(grid, source){
40374     this.grid = grid;
40375     this.store = new Roo.data.Store({
40376         recordType : Roo.grid.PropertyRecord
40377     });
40378     this.store.on('update', this.onUpdate,  this);
40379     if(source){
40380         this.setSource(source);
40381     }
40382     Roo.grid.PropertyStore.superclass.constructor.call(this);
40383 };
40384
40385
40386
40387 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40388     setSource : function(o){
40389         this.source = o;
40390         this.store.removeAll();
40391         var data = [];
40392         for(var k in o){
40393             if(this.isEditableValue(o[k])){
40394                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40395             }
40396         }
40397         this.store.loadRecords({records: data}, {}, true);
40398     },
40399
40400     onUpdate : function(ds, record, type){
40401         if(type == Roo.data.Record.EDIT){
40402             var v = record.data['value'];
40403             var oldValue = record.modified['value'];
40404             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40405                 this.source[record.id] = v;
40406                 record.commit();
40407                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40408             }else{
40409                 record.reject();
40410             }
40411         }
40412     },
40413
40414     getProperty : function(row){
40415        return this.store.getAt(row);
40416     },
40417
40418     isEditableValue: function(val){
40419         if(val && val instanceof Date){
40420             return true;
40421         }else if(typeof val == 'object' || typeof val == 'function'){
40422             return false;
40423         }
40424         return true;
40425     },
40426
40427     setValue : function(prop, value){
40428         this.source[prop] = value;
40429         this.store.getById(prop).set('value', value);
40430     },
40431
40432     getSource : function(){
40433         return this.source;
40434     }
40435 });
40436
40437 Roo.grid.PropertyColumnModel = function(grid, store){
40438     this.grid = grid;
40439     var g = Roo.grid;
40440     g.PropertyColumnModel.superclass.constructor.call(this, [
40441         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40442         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40443     ]);
40444     this.store = store;
40445     this.bselect = Roo.DomHelper.append(document.body, {
40446         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40447             {tag: 'option', value: 'true', html: 'true'},
40448             {tag: 'option', value: 'false', html: 'false'}
40449         ]
40450     });
40451     Roo.id(this.bselect);
40452     var f = Roo.form;
40453     this.editors = {
40454         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40455         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40456         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40457         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40458         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40459     };
40460     this.renderCellDelegate = this.renderCell.createDelegate(this);
40461     this.renderPropDelegate = this.renderProp.createDelegate(this);
40462 };
40463
40464 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40465     
40466     
40467     nameText : 'Name',
40468     valueText : 'Value',
40469     
40470     dateFormat : 'm/j/Y',
40471     
40472     
40473     renderDate : function(dateVal){
40474         return dateVal.dateFormat(this.dateFormat);
40475     },
40476
40477     renderBool : function(bVal){
40478         return bVal ? 'true' : 'false';
40479     },
40480
40481     isCellEditable : function(colIndex, rowIndex){
40482         return colIndex == 1;
40483     },
40484
40485     getRenderer : function(col){
40486         return col == 1 ?
40487             this.renderCellDelegate : this.renderPropDelegate;
40488     },
40489
40490     renderProp : function(v){
40491         return this.getPropertyName(v);
40492     },
40493
40494     renderCell : function(val){
40495         var rv = val;
40496         if(val instanceof Date){
40497             rv = this.renderDate(val);
40498         }else if(typeof val == 'boolean'){
40499             rv = this.renderBool(val);
40500         }
40501         return Roo.util.Format.htmlEncode(rv);
40502     },
40503
40504     getPropertyName : function(name){
40505         var pn = this.grid.propertyNames;
40506         return pn && pn[name] ? pn[name] : name;
40507     },
40508
40509     getCellEditor : function(colIndex, rowIndex){
40510         var p = this.store.getProperty(rowIndex);
40511         var n = p.data['name'], val = p.data['value'];
40512         
40513         if(typeof(this.grid.customEditors[n]) == 'string'){
40514             return this.editors[this.grid.customEditors[n]];
40515         }
40516         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40517             return this.grid.customEditors[n];
40518         }
40519         if(val instanceof Date){
40520             return this.editors['date'];
40521         }else if(typeof val == 'number'){
40522             return this.editors['number'];
40523         }else if(typeof val == 'boolean'){
40524             return this.editors['boolean'];
40525         }else{
40526             return this.editors['string'];
40527         }
40528     }
40529 });
40530
40531 /**
40532  * @class Roo.grid.PropertyGrid
40533  * @extends Roo.grid.EditorGrid
40534  * This class represents the  interface of a component based property grid control.
40535  * <br><br>Usage:<pre><code>
40536  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40537       
40538  });
40539  // set any options
40540  grid.render();
40541  * </code></pre>
40542   
40543  * @constructor
40544  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40545  * The container MUST have some type of size defined for the grid to fill. The container will be
40546  * automatically set to position relative if it isn't already.
40547  * @param {Object} config A config object that sets properties on this grid.
40548  */
40549 Roo.grid.PropertyGrid = function(container, config){
40550     config = config || {};
40551     var store = new Roo.grid.PropertyStore(this);
40552     this.store = store;
40553     var cm = new Roo.grid.PropertyColumnModel(this, store);
40554     store.store.sort('name', 'ASC');
40555     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40556         ds: store.store,
40557         cm: cm,
40558         enableColLock:false,
40559         enableColumnMove:false,
40560         stripeRows:false,
40561         trackMouseOver: false,
40562         clicksToEdit:1
40563     }, config));
40564     this.getGridEl().addClass('x-props-grid');
40565     this.lastEditRow = null;
40566     this.on('columnresize', this.onColumnResize, this);
40567     this.addEvents({
40568          /**
40569              * @event beforepropertychange
40570              * Fires before a property changes (return false to stop?)
40571              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40572              * @param {String} id Record Id
40573              * @param {String} newval New Value
40574          * @param {String} oldval Old Value
40575              */
40576         "beforepropertychange": true,
40577         /**
40578              * @event propertychange
40579              * Fires after a property changes
40580              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40581              * @param {String} id Record Id
40582              * @param {String} newval New Value
40583          * @param {String} oldval Old Value
40584              */
40585         "propertychange": true
40586     });
40587     this.customEditors = this.customEditors || {};
40588 };
40589 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40590     
40591      /**
40592      * @cfg {Object} customEditors map of colnames=> custom editors.
40593      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40594      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40595      * false disables editing of the field.
40596          */
40597     
40598       /**
40599      * @cfg {Object} propertyNames map of property Names to their displayed value
40600          */
40601     
40602     render : function(){
40603         Roo.grid.PropertyGrid.superclass.render.call(this);
40604         this.autoSize.defer(100, this);
40605     },
40606
40607     autoSize : function(){
40608         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40609         if(this.view){
40610             this.view.fitColumns();
40611         }
40612     },
40613
40614     onColumnResize : function(){
40615         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40616         this.autoSize();
40617     },
40618     /**
40619      * Sets the data for the Grid
40620      * accepts a Key => Value object of all the elements avaiable.
40621      * @param {Object} data  to appear in grid.
40622      */
40623     setSource : function(source){
40624         this.store.setSource(source);
40625         //this.autoSize();
40626     },
40627     /**
40628      * Gets all the data from the grid.
40629      * @return {Object} data  data stored in grid
40630      */
40631     getSource : function(){
40632         return this.store.getSource();
40633     }
40634 });/*
40635   
40636  * Licence LGPL
40637  
40638  */
40639  
40640 /**
40641  * @class Roo.grid.Calendar
40642  * @extends Roo.util.Grid
40643  * This class extends the Grid to provide a calendar widget
40644  * <br><br>Usage:<pre><code>
40645  var grid = new Roo.grid.Calendar("my-container-id", {
40646      ds: myDataStore,
40647      cm: myColModel,
40648      selModel: mySelectionModel,
40649      autoSizeColumns: true,
40650      monitorWindowResize: false,
40651      trackMouseOver: true
40652      eventstore : real data store..
40653  });
40654  // set any options
40655  grid.render();
40656   
40657   * @constructor
40658  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40659  * The container MUST have some type of size defined for the grid to fill. The container will be
40660  * automatically set to position relative if it isn't already.
40661  * @param {Object} config A config object that sets properties on this grid.
40662  */
40663 Roo.grid.Calendar = function(container, config){
40664         // initialize the container
40665         this.container = Roo.get(container);
40666         this.container.update("");
40667         this.container.setStyle("overflow", "hidden");
40668     this.container.addClass('x-grid-container');
40669
40670     this.id = this.container.id;
40671
40672     Roo.apply(this, config);
40673     // check and correct shorthanded configs
40674     
40675     var rows = [];
40676     var d =1;
40677     for (var r = 0;r < 6;r++) {
40678         
40679         rows[r]=[];
40680         for (var c =0;c < 7;c++) {
40681             rows[r][c]= '';
40682         }
40683     }
40684     if (this.eventStore) {
40685         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40686         this.eventStore.on('load',this.onLoad, this);
40687         this.eventStore.on('beforeload',this.clearEvents, this);
40688          
40689     }
40690     
40691     this.dataSource = new Roo.data.Store({
40692             proxy: new Roo.data.MemoryProxy(rows),
40693             reader: new Roo.data.ArrayReader({}, [
40694                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40695     });
40696
40697     this.dataSource.load();
40698     this.ds = this.dataSource;
40699     this.ds.xmodule = this.xmodule || false;
40700     
40701     
40702     var cellRender = function(v,x,r)
40703     {
40704         return String.format(
40705             '<div class="fc-day  fc-widget-content"><div>' +
40706                 '<div class="fc-event-container"></div>' +
40707                 '<div class="fc-day-number">{0}</div>'+
40708                 
40709                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40710             '</div></div>', v);
40711     
40712     }
40713     
40714     
40715     this.colModel = new Roo.grid.ColumnModel( [
40716         {
40717             xtype: 'ColumnModel',
40718             xns: Roo.grid,
40719             dataIndex : 'weekday0',
40720             header : 'Sunday',
40721             renderer : cellRender
40722         },
40723         {
40724             xtype: 'ColumnModel',
40725             xns: Roo.grid,
40726             dataIndex : 'weekday1',
40727             header : 'Monday',
40728             renderer : cellRender
40729         },
40730         {
40731             xtype: 'ColumnModel',
40732             xns: Roo.grid,
40733             dataIndex : 'weekday2',
40734             header : 'Tuesday',
40735             renderer : cellRender
40736         },
40737         {
40738             xtype: 'ColumnModel',
40739             xns: Roo.grid,
40740             dataIndex : 'weekday3',
40741             header : 'Wednesday',
40742             renderer : cellRender
40743         },
40744         {
40745             xtype: 'ColumnModel',
40746             xns: Roo.grid,
40747             dataIndex : 'weekday4',
40748             header : 'Thursday',
40749             renderer : cellRender
40750         },
40751         {
40752             xtype: 'ColumnModel',
40753             xns: Roo.grid,
40754             dataIndex : 'weekday5',
40755             header : 'Friday',
40756             renderer : cellRender
40757         },
40758         {
40759             xtype: 'ColumnModel',
40760             xns: Roo.grid,
40761             dataIndex : 'weekday6',
40762             header : 'Saturday',
40763             renderer : cellRender
40764         }
40765     ]);
40766     this.cm = this.colModel;
40767     this.cm.xmodule = this.xmodule || false;
40768  
40769         
40770           
40771     //this.selModel = new Roo.grid.CellSelectionModel();
40772     //this.sm = this.selModel;
40773     //this.selModel.init(this);
40774     
40775     
40776     if(this.width){
40777         this.container.setWidth(this.width);
40778     }
40779
40780     if(this.height){
40781         this.container.setHeight(this.height);
40782     }
40783     /** @private */
40784         this.addEvents({
40785         // raw events
40786         /**
40787          * @event click
40788          * The raw click event for the entire grid.
40789          * @param {Roo.EventObject} e
40790          */
40791         "click" : true,
40792         /**
40793          * @event dblclick
40794          * The raw dblclick event for the entire grid.
40795          * @param {Roo.EventObject} e
40796          */
40797         "dblclick" : true,
40798         /**
40799          * @event contextmenu
40800          * The raw contextmenu event for the entire grid.
40801          * @param {Roo.EventObject} e
40802          */
40803         "contextmenu" : true,
40804         /**
40805          * @event mousedown
40806          * The raw mousedown event for the entire grid.
40807          * @param {Roo.EventObject} e
40808          */
40809         "mousedown" : true,
40810         /**
40811          * @event mouseup
40812          * The raw mouseup event for the entire grid.
40813          * @param {Roo.EventObject} e
40814          */
40815         "mouseup" : true,
40816         /**
40817          * @event mouseover
40818          * The raw mouseover event for the entire grid.
40819          * @param {Roo.EventObject} e
40820          */
40821         "mouseover" : true,
40822         /**
40823          * @event mouseout
40824          * The raw mouseout event for the entire grid.
40825          * @param {Roo.EventObject} e
40826          */
40827         "mouseout" : true,
40828         /**
40829          * @event keypress
40830          * The raw keypress event for the entire grid.
40831          * @param {Roo.EventObject} e
40832          */
40833         "keypress" : true,
40834         /**
40835          * @event keydown
40836          * The raw keydown event for the entire grid.
40837          * @param {Roo.EventObject} e
40838          */
40839         "keydown" : true,
40840
40841         // custom events
40842
40843         /**
40844          * @event cellclick
40845          * Fires when a cell is clicked
40846          * @param {Grid} this
40847          * @param {Number} rowIndex
40848          * @param {Number} columnIndex
40849          * @param {Roo.EventObject} e
40850          */
40851         "cellclick" : true,
40852         /**
40853          * @event celldblclick
40854          * Fires when a cell is double clicked
40855          * @param {Grid} this
40856          * @param {Number} rowIndex
40857          * @param {Number} columnIndex
40858          * @param {Roo.EventObject} e
40859          */
40860         "celldblclick" : true,
40861         /**
40862          * @event rowclick
40863          * Fires when a row is clicked
40864          * @param {Grid} this
40865          * @param {Number} rowIndex
40866          * @param {Roo.EventObject} e
40867          */
40868         "rowclick" : true,
40869         /**
40870          * @event rowdblclick
40871          * Fires when a row is double clicked
40872          * @param {Grid} this
40873          * @param {Number} rowIndex
40874          * @param {Roo.EventObject} e
40875          */
40876         "rowdblclick" : true,
40877         /**
40878          * @event headerclick
40879          * Fires when a header is clicked
40880          * @param {Grid} this
40881          * @param {Number} columnIndex
40882          * @param {Roo.EventObject} e
40883          */
40884         "headerclick" : true,
40885         /**
40886          * @event headerdblclick
40887          * Fires when a header cell is double clicked
40888          * @param {Grid} this
40889          * @param {Number} columnIndex
40890          * @param {Roo.EventObject} e
40891          */
40892         "headerdblclick" : true,
40893         /**
40894          * @event rowcontextmenu
40895          * Fires when a row is right clicked
40896          * @param {Grid} this
40897          * @param {Number} rowIndex
40898          * @param {Roo.EventObject} e
40899          */
40900         "rowcontextmenu" : true,
40901         /**
40902          * @event cellcontextmenu
40903          * Fires when a cell is right clicked
40904          * @param {Grid} this
40905          * @param {Number} rowIndex
40906          * @param {Number} cellIndex
40907          * @param {Roo.EventObject} e
40908          */
40909          "cellcontextmenu" : true,
40910         /**
40911          * @event headercontextmenu
40912          * Fires when a header is right clicked
40913          * @param {Grid} this
40914          * @param {Number} columnIndex
40915          * @param {Roo.EventObject} e
40916          */
40917         "headercontextmenu" : true,
40918         /**
40919          * @event bodyscroll
40920          * Fires when the body element is scrolled
40921          * @param {Number} scrollLeft
40922          * @param {Number} scrollTop
40923          */
40924         "bodyscroll" : true,
40925         /**
40926          * @event columnresize
40927          * Fires when the user resizes a column
40928          * @param {Number} columnIndex
40929          * @param {Number} newSize
40930          */
40931         "columnresize" : true,
40932         /**
40933          * @event columnmove
40934          * Fires when the user moves a column
40935          * @param {Number} oldIndex
40936          * @param {Number} newIndex
40937          */
40938         "columnmove" : true,
40939         /**
40940          * @event startdrag
40941          * Fires when row(s) start being dragged
40942          * @param {Grid} this
40943          * @param {Roo.GridDD} dd The drag drop object
40944          * @param {event} e The raw browser event
40945          */
40946         "startdrag" : true,
40947         /**
40948          * @event enddrag
40949          * Fires when a drag operation is complete
40950          * @param {Grid} this
40951          * @param {Roo.GridDD} dd The drag drop object
40952          * @param {event} e The raw browser event
40953          */
40954         "enddrag" : true,
40955         /**
40956          * @event dragdrop
40957          * Fires when dragged row(s) are dropped on a valid DD target
40958          * @param {Grid} this
40959          * @param {Roo.GridDD} dd The drag drop object
40960          * @param {String} targetId The target drag drop object
40961          * @param {event} e The raw browser event
40962          */
40963         "dragdrop" : true,
40964         /**
40965          * @event dragover
40966          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40967          * @param {Grid} this
40968          * @param {Roo.GridDD} dd The drag drop object
40969          * @param {String} targetId The target drag drop object
40970          * @param {event} e The raw browser event
40971          */
40972         "dragover" : true,
40973         /**
40974          * @event dragenter
40975          *  Fires when the dragged row(s) first cross another DD target while being dragged
40976          * @param {Grid} this
40977          * @param {Roo.GridDD} dd The drag drop object
40978          * @param {String} targetId The target drag drop object
40979          * @param {event} e The raw browser event
40980          */
40981         "dragenter" : true,
40982         /**
40983          * @event dragout
40984          * Fires when the dragged row(s) leave another DD target while being dragged
40985          * @param {Grid} this
40986          * @param {Roo.GridDD} dd The drag drop object
40987          * @param {String} targetId The target drag drop object
40988          * @param {event} e The raw browser event
40989          */
40990         "dragout" : true,
40991         /**
40992          * @event rowclass
40993          * Fires when a row is rendered, so you can change add a style to it.
40994          * @param {GridView} gridview   The grid view
40995          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40996          */
40997         'rowclass' : true,
40998
40999         /**
41000          * @event render
41001          * Fires when the grid is rendered
41002          * @param {Grid} grid
41003          */
41004         'render' : true,
41005             /**
41006              * @event select
41007              * Fires when a date is selected
41008              * @param {DatePicker} this
41009              * @param {Date} date The selected date
41010              */
41011         'select': true,
41012         /**
41013              * @event monthchange
41014              * Fires when the displayed month changes 
41015              * @param {DatePicker} this
41016              * @param {Date} date The selected month
41017              */
41018         'monthchange': true,
41019         /**
41020              * @event evententer
41021              * Fires when mouse over an event
41022              * @param {Calendar} this
41023              * @param {event} Event
41024              */
41025         'evententer': true,
41026         /**
41027              * @event eventleave
41028              * Fires when the mouse leaves an
41029              * @param {Calendar} this
41030              * @param {event}
41031              */
41032         'eventleave': true,
41033         /**
41034              * @event eventclick
41035              * Fires when the mouse click an
41036              * @param {Calendar} this
41037              * @param {event}
41038              */
41039         'eventclick': true,
41040         /**
41041              * @event eventrender
41042              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41043              * @param {Calendar} this
41044              * @param {data} data to be modified
41045              */
41046         'eventrender': true
41047         
41048     });
41049
41050     Roo.grid.Grid.superclass.constructor.call(this);
41051     this.on('render', function() {
41052         this.view.el.addClass('x-grid-cal'); 
41053         
41054         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41055
41056     },this);
41057     
41058     if (!Roo.grid.Calendar.style) {
41059         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41060             
41061             
41062             '.x-grid-cal .x-grid-col' :  {
41063                 height: 'auto !important',
41064                 'vertical-align': 'top'
41065             },
41066             '.x-grid-cal  .fc-event-hori' : {
41067                 height: '14px'
41068             }
41069              
41070             
41071         }, Roo.id());
41072     }
41073
41074     
41075     
41076 };
41077 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41078     /**
41079      * @cfg {Store} eventStore The store that loads events.
41080      */
41081     eventStore : 25,
41082
41083      
41084     activeDate : false,
41085     startDay : 0,
41086     autoWidth : true,
41087     monitorWindowResize : false,
41088
41089     
41090     resizeColumns : function() {
41091         var col = (this.view.el.getWidth() / 7) - 3;
41092         // loop through cols, and setWidth
41093         for(var i =0 ; i < 7 ; i++){
41094             this.cm.setColumnWidth(i, col);
41095         }
41096     },
41097      setDate :function(date) {
41098         
41099         Roo.log('setDate?');
41100         
41101         this.resizeColumns();
41102         var vd = this.activeDate;
41103         this.activeDate = date;
41104 //        if(vd && this.el){
41105 //            var t = date.getTime();
41106 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41107 //                Roo.log('using add remove');
41108 //                
41109 //                this.fireEvent('monthchange', this, date);
41110 //                
41111 //                this.cells.removeClass("fc-state-highlight");
41112 //                this.cells.each(function(c){
41113 //                   if(c.dateValue == t){
41114 //                       c.addClass("fc-state-highlight");
41115 //                       setTimeout(function(){
41116 //                            try{c.dom.firstChild.focus();}catch(e){}
41117 //                       }, 50);
41118 //                       return false;
41119 //                   }
41120 //                   return true;
41121 //                });
41122 //                return;
41123 //            }
41124 //        }
41125         
41126         var days = date.getDaysInMonth();
41127         
41128         var firstOfMonth = date.getFirstDateOfMonth();
41129         var startingPos = firstOfMonth.getDay()-this.startDay;
41130         
41131         if(startingPos < this.startDay){
41132             startingPos += 7;
41133         }
41134         
41135         var pm = date.add(Date.MONTH, -1);
41136         var prevStart = pm.getDaysInMonth()-startingPos;
41137 //        
41138         
41139         
41140         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41141         
41142         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41143         //this.cells.addClassOnOver('fc-state-hover');
41144         
41145         var cells = this.cells.elements;
41146         var textEls = this.textNodes;
41147         
41148         //Roo.each(cells, function(cell){
41149         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41150         //});
41151         
41152         days += startingPos;
41153
41154         // convert everything to numbers so it's fast
41155         var day = 86400000;
41156         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41157         //Roo.log(d);
41158         //Roo.log(pm);
41159         //Roo.log(prevStart);
41160         
41161         var today = new Date().clearTime().getTime();
41162         var sel = date.clearTime().getTime();
41163         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41164         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41165         var ddMatch = this.disabledDatesRE;
41166         var ddText = this.disabledDatesText;
41167         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41168         var ddaysText = this.disabledDaysText;
41169         var format = this.format;
41170         
41171         var setCellClass = function(cal, cell){
41172             
41173             //Roo.log('set Cell Class');
41174             cell.title = "";
41175             var t = d.getTime();
41176             
41177             //Roo.log(d);
41178             
41179             
41180             cell.dateValue = t;
41181             if(t == today){
41182                 cell.className += " fc-today";
41183                 cell.className += " fc-state-highlight";
41184                 cell.title = cal.todayText;
41185             }
41186             if(t == sel){
41187                 // disable highlight in other month..
41188                 cell.className += " fc-state-highlight";
41189                 
41190             }
41191             // disabling
41192             if(t < min) {
41193                 //cell.className = " fc-state-disabled";
41194                 cell.title = cal.minText;
41195                 return;
41196             }
41197             if(t > max) {
41198                 //cell.className = " fc-state-disabled";
41199                 cell.title = cal.maxText;
41200                 return;
41201             }
41202             if(ddays){
41203                 if(ddays.indexOf(d.getDay()) != -1){
41204                     // cell.title = ddaysText;
41205                    // cell.className = " fc-state-disabled";
41206                 }
41207             }
41208             if(ddMatch && format){
41209                 var fvalue = d.dateFormat(format);
41210                 if(ddMatch.test(fvalue)){
41211                     cell.title = ddText.replace("%0", fvalue);
41212                    cell.className = " fc-state-disabled";
41213                 }
41214             }
41215             
41216             if (!cell.initialClassName) {
41217                 cell.initialClassName = cell.dom.className;
41218             }
41219             
41220             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41221         };
41222
41223         var i = 0;
41224         
41225         for(; i < startingPos; i++) {
41226             cells[i].dayName =  (++prevStart);
41227             Roo.log(textEls[i]);
41228             d.setDate(d.getDate()+1);
41229             
41230             //cells[i].className = "fc-past fc-other-month";
41231             setCellClass(this, cells[i]);
41232         }
41233         
41234         var intDay = 0;
41235         
41236         for(; i < days; i++){
41237             intDay = i - startingPos + 1;
41238             cells[i].dayName =  (intDay);
41239             d.setDate(d.getDate()+1);
41240             
41241             cells[i].className = ''; // "x-date-active";
41242             setCellClass(this, cells[i]);
41243         }
41244         var extraDays = 0;
41245         
41246         for(; i < 42; i++) {
41247             //textEls[i].innerHTML = (++extraDays);
41248             
41249             d.setDate(d.getDate()+1);
41250             cells[i].dayName = (++extraDays);
41251             cells[i].className = "fc-future fc-other-month";
41252             setCellClass(this, cells[i]);
41253         }
41254         
41255         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41256         
41257         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41258         
41259         // this will cause all the cells to mis
41260         var rows= [];
41261         var i =0;
41262         for (var r = 0;r < 6;r++) {
41263             for (var c =0;c < 7;c++) {
41264                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41265             }    
41266         }
41267         
41268         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41269         for(i=0;i<cells.length;i++) {
41270             
41271             this.cells.elements[i].dayName = cells[i].dayName ;
41272             this.cells.elements[i].className = cells[i].className;
41273             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41274             this.cells.elements[i].title = cells[i].title ;
41275             this.cells.elements[i].dateValue = cells[i].dateValue ;
41276         }
41277         
41278         
41279         
41280         
41281         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41282         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41283         
41284         ////if(totalRows != 6){
41285             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41286            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41287        // }
41288         
41289         this.fireEvent('monthchange', this, date);
41290         
41291         
41292     },
41293  /**
41294      * Returns the grid's SelectionModel.
41295      * @return {SelectionModel}
41296      */
41297     getSelectionModel : function(){
41298         if(!this.selModel){
41299             this.selModel = new Roo.grid.CellSelectionModel();
41300         }
41301         return this.selModel;
41302     },
41303
41304     load: function() {
41305         this.eventStore.load()
41306         
41307         
41308         
41309     },
41310     
41311     findCell : function(dt) {
41312         dt = dt.clearTime().getTime();
41313         var ret = false;
41314         this.cells.each(function(c){
41315             //Roo.log("check " +c.dateValue + '?=' + dt);
41316             if(c.dateValue == dt){
41317                 ret = c;
41318                 return false;
41319             }
41320             return true;
41321         });
41322         
41323         return ret;
41324     },
41325     
41326     findCells : function(rec) {
41327         var s = rec.data.start_dt.clone().clearTime().getTime();
41328        // Roo.log(s);
41329         var e= rec.data.end_dt.clone().clearTime().getTime();
41330        // Roo.log(e);
41331         var ret = [];
41332         this.cells.each(function(c){
41333              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41334             
41335             if(c.dateValue > e){
41336                 return ;
41337             }
41338             if(c.dateValue < s){
41339                 return ;
41340             }
41341             ret.push(c);
41342         });
41343         
41344         return ret;    
41345     },
41346     
41347     findBestRow: function(cells)
41348     {
41349         var ret = 0;
41350         
41351         for (var i =0 ; i < cells.length;i++) {
41352             ret  = Math.max(cells[i].rows || 0,ret);
41353         }
41354         return ret;
41355         
41356     },
41357     
41358     
41359     addItem : function(rec)
41360     {
41361         // look for vertical location slot in
41362         var cells = this.findCells(rec);
41363         
41364         rec.row = this.findBestRow(cells);
41365         
41366         // work out the location.
41367         
41368         var crow = false;
41369         var rows = [];
41370         for(var i =0; i < cells.length; i++) {
41371             if (!crow) {
41372                 crow = {
41373                     start : cells[i],
41374                     end :  cells[i]
41375                 };
41376                 continue;
41377             }
41378             if (crow.start.getY() == cells[i].getY()) {
41379                 // on same row.
41380                 crow.end = cells[i];
41381                 continue;
41382             }
41383             // different row.
41384             rows.push(crow);
41385             crow = {
41386                 start: cells[i],
41387                 end : cells[i]
41388             };
41389             
41390         }
41391         
41392         rows.push(crow);
41393         rec.els = [];
41394         rec.rows = rows;
41395         rec.cells = cells;
41396         for (var i = 0; i < cells.length;i++) {
41397             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41398             
41399         }
41400         
41401         
41402     },
41403     
41404     clearEvents: function() {
41405         
41406         if (!this.eventStore.getCount()) {
41407             return;
41408         }
41409         // reset number of rows in cells.
41410         Roo.each(this.cells.elements, function(c){
41411             c.rows = 0;
41412         });
41413         
41414         this.eventStore.each(function(e) {
41415             this.clearEvent(e);
41416         },this);
41417         
41418     },
41419     
41420     clearEvent : function(ev)
41421     {
41422         if (ev.els) {
41423             Roo.each(ev.els, function(el) {
41424                 el.un('mouseenter' ,this.onEventEnter, this);
41425                 el.un('mouseleave' ,this.onEventLeave, this);
41426                 el.remove();
41427             },this);
41428             ev.els = [];
41429         }
41430     },
41431     
41432     
41433     renderEvent : function(ev,ctr) {
41434         if (!ctr) {
41435              ctr = this.view.el.select('.fc-event-container',true).first();
41436         }
41437         
41438          
41439         this.clearEvent(ev);
41440             //code
41441        
41442         
41443         
41444         ev.els = [];
41445         var cells = ev.cells;
41446         var rows = ev.rows;
41447         this.fireEvent('eventrender', this, ev);
41448         
41449         for(var i =0; i < rows.length; i++) {
41450             
41451             cls = '';
41452             if (i == 0) {
41453                 cls += ' fc-event-start';
41454             }
41455             if ((i+1) == rows.length) {
41456                 cls += ' fc-event-end';
41457             }
41458             
41459             //Roo.log(ev.data);
41460             // how many rows should it span..
41461             var cg = this.eventTmpl.append(ctr,Roo.apply({
41462                 fccls : cls
41463                 
41464             }, ev.data) , true);
41465             
41466             
41467             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41468             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41469             cg.on('click', this.onEventClick, this, ev);
41470             
41471             ev.els.push(cg);
41472             
41473             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41474             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41475             //Roo.log(cg);
41476              
41477             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41478             cg.setWidth(ebox.right - sbox.x -2);
41479         }
41480     },
41481     
41482     renderEvents: function()
41483     {   
41484         // first make sure there is enough space..
41485         
41486         if (!this.eventTmpl) {
41487             this.eventTmpl = new Roo.Template(
41488                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41489                     '<div class="fc-event-inner">' +
41490                         '<span class="fc-event-time">{time}</span>' +
41491                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41492                     '</div>' +
41493                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41494                 '</div>'
41495             );
41496                 
41497         }
41498                
41499         
41500         
41501         this.cells.each(function(c) {
41502             //Roo.log(c.select('.fc-day-content div',true).first());
41503             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41504         });
41505         
41506         var ctr = this.view.el.select('.fc-event-container',true).first();
41507         
41508         var cls;
41509         this.eventStore.each(function(ev){
41510             
41511             this.renderEvent(ev);
41512              
41513              
41514         }, this);
41515         this.view.layout();
41516         
41517     },
41518     
41519     onEventEnter: function (e, el,event,d) {
41520         this.fireEvent('evententer', this, el, event);
41521     },
41522     
41523     onEventLeave: function (e, el,event,d) {
41524         this.fireEvent('eventleave', this, el, event);
41525     },
41526     
41527     onEventClick: function (e, el,event,d) {
41528         this.fireEvent('eventclick', this, el, event);
41529     },
41530     
41531     onMonthChange: function () {
41532         this.store.load();
41533     },
41534     
41535     onLoad: function () {
41536         
41537         //Roo.log('calendar onload');
41538 //         
41539         if(this.eventStore.getCount() > 0){
41540             
41541            
41542             
41543             this.eventStore.each(function(d){
41544                 
41545                 
41546                 // FIXME..
41547                 var add =   d.data;
41548                 if (typeof(add.end_dt) == 'undefined')  {
41549                     Roo.log("Missing End time in calendar data: ");
41550                     Roo.log(d);
41551                     return;
41552                 }
41553                 if (typeof(add.start_dt) == 'undefined')  {
41554                     Roo.log("Missing Start time in calendar data: ");
41555                     Roo.log(d);
41556                     return;
41557                 }
41558                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41559                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41560                 add.id = add.id || d.id;
41561                 add.title = add.title || '??';
41562                 
41563                 this.addItem(d);
41564                 
41565              
41566             },this);
41567         }
41568         
41569         this.renderEvents();
41570     }
41571     
41572
41573 });
41574 /*
41575  grid : {
41576                 xtype: 'Grid',
41577                 xns: Roo.grid,
41578                 listeners : {
41579                     render : function ()
41580                     {
41581                         _this.grid = this;
41582                         
41583                         if (!this.view.el.hasClass('course-timesheet')) {
41584                             this.view.el.addClass('course-timesheet');
41585                         }
41586                         if (this.tsStyle) {
41587                             this.ds.load({});
41588                             return; 
41589                         }
41590                         Roo.log('width');
41591                         Roo.log(_this.grid.view.el.getWidth());
41592                         
41593                         
41594                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41595                             '.course-timesheet .x-grid-row' : {
41596                                 height: '80px'
41597                             },
41598                             '.x-grid-row td' : {
41599                                 'vertical-align' : 0
41600                             },
41601                             '.course-edit-link' : {
41602                                 'color' : 'blue',
41603                                 'text-overflow' : 'ellipsis',
41604                                 'overflow' : 'hidden',
41605                                 'white-space' : 'nowrap',
41606                                 'cursor' : 'pointer'
41607                             },
41608                             '.sub-link' : {
41609                                 'color' : 'green'
41610                             },
41611                             '.de-act-sup-link' : {
41612                                 'color' : 'purple',
41613                                 'text-decoration' : 'line-through'
41614                             },
41615                             '.de-act-link' : {
41616                                 'color' : 'red',
41617                                 'text-decoration' : 'line-through'
41618                             },
41619                             '.course-timesheet .course-highlight' : {
41620                                 'border-top-style': 'dashed !important',
41621                                 'border-bottom-bottom': 'dashed !important'
41622                             },
41623                             '.course-timesheet .course-item' : {
41624                                 'font-family'   : 'tahoma, arial, helvetica',
41625                                 'font-size'     : '11px',
41626                                 'overflow'      : 'hidden',
41627                                 'padding-left'  : '10px',
41628                                 'padding-right' : '10px',
41629                                 'padding-top' : '10px' 
41630                             }
41631                             
41632                         }, Roo.id());
41633                                 this.ds.load({});
41634                     }
41635                 },
41636                 autoWidth : true,
41637                 monitorWindowResize : false,
41638                 cellrenderer : function(v,x,r)
41639                 {
41640                     return v;
41641                 },
41642                 sm : {
41643                     xtype: 'CellSelectionModel',
41644                     xns: Roo.grid
41645                 },
41646                 dataSource : {
41647                     xtype: 'Store',
41648                     xns: Roo.data,
41649                     listeners : {
41650                         beforeload : function (_self, options)
41651                         {
41652                             options.params = options.params || {};
41653                             options.params._month = _this.monthField.getValue();
41654                             options.params.limit = 9999;
41655                             options.params['sort'] = 'when_dt';    
41656                             options.params['dir'] = 'ASC';    
41657                             this.proxy.loadResponse = this.loadResponse;
41658                             Roo.log("load?");
41659                             //this.addColumns();
41660                         },
41661                         load : function (_self, records, options)
41662                         {
41663                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41664                                 // if you click on the translation.. you can edit it...
41665                                 var el = Roo.get(this);
41666                                 var id = el.dom.getAttribute('data-id');
41667                                 var d = el.dom.getAttribute('data-date');
41668                                 var t = el.dom.getAttribute('data-time');
41669                                 //var id = this.child('span').dom.textContent;
41670                                 
41671                                 //Roo.log(this);
41672                                 Pman.Dialog.CourseCalendar.show({
41673                                     id : id,
41674                                     when_d : d,
41675                                     when_t : t,
41676                                     productitem_active : id ? 1 : 0
41677                                 }, function() {
41678                                     _this.grid.ds.load({});
41679                                 });
41680                            
41681                            });
41682                            
41683                            _this.panel.fireEvent('resize', [ '', '' ]);
41684                         }
41685                     },
41686                     loadResponse : function(o, success, response){
41687                             // this is overridden on before load..
41688                             
41689                             Roo.log("our code?");       
41690                             //Roo.log(success);
41691                             //Roo.log(response)
41692                             delete this.activeRequest;
41693                             if(!success){
41694                                 this.fireEvent("loadexception", this, o, response);
41695                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41696                                 return;
41697                             }
41698                             var result;
41699                             try {
41700                                 result = o.reader.read(response);
41701                             }catch(e){
41702                                 Roo.log("load exception?");
41703                                 this.fireEvent("loadexception", this, o, response, e);
41704                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41705                                 return;
41706                             }
41707                             Roo.log("ready...");        
41708                             // loop through result.records;
41709                             // and set this.tdate[date] = [] << array of records..
41710                             _this.tdata  = {};
41711                             Roo.each(result.records, function(r){
41712                                 //Roo.log(r.data);
41713                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41714                                     _this.tdata[r.data.when_dt.format('j')] = [];
41715                                 }
41716                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41717                             });
41718                             
41719                             //Roo.log(_this.tdata);
41720                             
41721                             result.records = [];
41722                             result.totalRecords = 6;
41723                     
41724                             // let's generate some duumy records for the rows.
41725                             //var st = _this.dateField.getValue();
41726                             
41727                             // work out monday..
41728                             //st = st.add(Date.DAY, -1 * st.format('w'));
41729                             
41730                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41731                             
41732                             var firstOfMonth = date.getFirstDayOfMonth();
41733                             var days = date.getDaysInMonth();
41734                             var d = 1;
41735                             var firstAdded = false;
41736                             for (var i = 0; i < result.totalRecords ; i++) {
41737                                 //var d= st.add(Date.DAY, i);
41738                                 var row = {};
41739                                 var added = 0;
41740                                 for(var w = 0 ; w < 7 ; w++){
41741                                     if(!firstAdded && firstOfMonth != w){
41742                                         continue;
41743                                     }
41744                                     if(d > days){
41745                                         continue;
41746                                     }
41747                                     firstAdded = true;
41748                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41749                                     row['weekday'+w] = String.format(
41750                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41751                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41752                                                     d,
41753                                                     date.format('Y-m-')+dd
41754                                                 );
41755                                     added++;
41756                                     if(typeof(_this.tdata[d]) != 'undefined'){
41757                                         Roo.each(_this.tdata[d], function(r){
41758                                             var is_sub = '';
41759                                             var deactive = '';
41760                                             var id = r.id;
41761                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41762                                             if(r.parent_id*1>0){
41763                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41764                                                 id = r.parent_id;
41765                                             }
41766                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41767                                                 deactive = 'de-act-link';
41768                                             }
41769                                             
41770                                             row['weekday'+w] += String.format(
41771                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41772                                                     id, //0
41773                                                     r.product_id_name, //1
41774                                                     r.when_dt.format('h:ia'), //2
41775                                                     is_sub, //3
41776                                                     deactive, //4
41777                                                     desc // 5
41778                                             );
41779                                         });
41780                                     }
41781                                     d++;
41782                                 }
41783                                 
41784                                 // only do this if something added..
41785                                 if(added > 0){ 
41786                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41787                                 }
41788                                 
41789                                 
41790                                 // push it twice. (second one with an hour..
41791                                 
41792                             }
41793                             //Roo.log(result);
41794                             this.fireEvent("load", this, o, o.request.arg);
41795                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41796                         },
41797                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41798                     proxy : {
41799                         xtype: 'HttpProxy',
41800                         xns: Roo.data,
41801                         method : 'GET',
41802                         url : baseURL + '/Roo/Shop_course.php'
41803                     },
41804                     reader : {
41805                         xtype: 'JsonReader',
41806                         xns: Roo.data,
41807                         id : 'id',
41808                         fields : [
41809                             {
41810                                 'name': 'id',
41811                                 'type': 'int'
41812                             },
41813                             {
41814                                 'name': 'when_dt',
41815                                 'type': 'string'
41816                             },
41817                             {
41818                                 'name': 'end_dt',
41819                                 'type': 'string'
41820                             },
41821                             {
41822                                 'name': 'parent_id',
41823                                 'type': 'int'
41824                             },
41825                             {
41826                                 'name': 'product_id',
41827                                 'type': 'int'
41828                             },
41829                             {
41830                                 'name': 'productitem_id',
41831                                 'type': 'int'
41832                             },
41833                             {
41834                                 'name': 'guid',
41835                                 'type': 'int'
41836                             }
41837                         ]
41838                     }
41839                 },
41840                 toolbar : {
41841                     xtype: 'Toolbar',
41842                     xns: Roo,
41843                     items : [
41844                         {
41845                             xtype: 'Button',
41846                             xns: Roo.Toolbar,
41847                             listeners : {
41848                                 click : function (_self, e)
41849                                 {
41850                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41851                                     sd.setMonth(sd.getMonth()-1);
41852                                     _this.monthField.setValue(sd.format('Y-m-d'));
41853                                     _this.grid.ds.load({});
41854                                 }
41855                             },
41856                             text : "Back"
41857                         },
41858                         {
41859                             xtype: 'Separator',
41860                             xns: Roo.Toolbar
41861                         },
41862                         {
41863                             xtype: 'MonthField',
41864                             xns: Roo.form,
41865                             listeners : {
41866                                 render : function (_self)
41867                                 {
41868                                     _this.monthField = _self;
41869                                    // _this.monthField.set  today
41870                                 },
41871                                 select : function (combo, date)
41872                                 {
41873                                     _this.grid.ds.load({});
41874                                 }
41875                             },
41876                             value : (function() { return new Date(); })()
41877                         },
41878                         {
41879                             xtype: 'Separator',
41880                             xns: Roo.Toolbar
41881                         },
41882                         {
41883                             xtype: 'TextItem',
41884                             xns: Roo.Toolbar,
41885                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41886                         },
41887                         {
41888                             xtype: 'Fill',
41889                             xns: Roo.Toolbar
41890                         },
41891                         {
41892                             xtype: 'Button',
41893                             xns: Roo.Toolbar,
41894                             listeners : {
41895                                 click : function (_self, e)
41896                                 {
41897                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41898                                     sd.setMonth(sd.getMonth()+1);
41899                                     _this.monthField.setValue(sd.format('Y-m-d'));
41900                                     _this.grid.ds.load({});
41901                                 }
41902                             },
41903                             text : "Next"
41904                         }
41905                     ]
41906                 },
41907                  
41908             }
41909         };
41910         
41911         *//*
41912  * Based on:
41913  * Ext JS Library 1.1.1
41914  * Copyright(c) 2006-2007, Ext JS, LLC.
41915  *
41916  * Originally Released Under LGPL - original licence link has changed is not relivant.
41917  *
41918  * Fork - LGPL
41919  * <script type="text/javascript">
41920  */
41921  
41922 /**
41923  * @class Roo.LoadMask
41924  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41925  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41926  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41927  * element's UpdateManager load indicator and will be destroyed after the initial load.
41928  * @constructor
41929  * Create a new LoadMask
41930  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41931  * @param {Object} config The config object
41932  */
41933 Roo.LoadMask = function(el, config){
41934     this.el = Roo.get(el);
41935     Roo.apply(this, config);
41936     if(this.store){
41937         this.store.on('beforeload', this.onBeforeLoad, this);
41938         this.store.on('load', this.onLoad, this);
41939         this.store.on('loadexception', this.onLoadException, this);
41940         this.removeMask = false;
41941     }else{
41942         var um = this.el.getUpdateManager();
41943         um.showLoadIndicator = false; // disable the default indicator
41944         um.on('beforeupdate', this.onBeforeLoad, this);
41945         um.on('update', this.onLoad, this);
41946         um.on('failure', this.onLoad, this);
41947         this.removeMask = true;
41948     }
41949 };
41950
41951 Roo.LoadMask.prototype = {
41952     /**
41953      * @cfg {Boolean} removeMask
41954      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41955      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41956      */
41957     /**
41958      * @cfg {String} msg
41959      * The text to display in a centered loading message box (defaults to 'Loading...')
41960      */
41961     msg : 'Loading...',
41962     /**
41963      * @cfg {String} msgCls
41964      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41965      */
41966     msgCls : 'x-mask-loading',
41967
41968     /**
41969      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41970      * @type Boolean
41971      */
41972     disabled: false,
41973
41974     /**
41975      * Disables the mask to prevent it from being displayed
41976      */
41977     disable : function(){
41978        this.disabled = true;
41979     },
41980
41981     /**
41982      * Enables the mask so that it can be displayed
41983      */
41984     enable : function(){
41985         this.disabled = false;
41986     },
41987     
41988     onLoadException : function()
41989     {
41990         Roo.log(arguments);
41991         
41992         if (typeof(arguments[3]) != 'undefined') {
41993             Roo.MessageBox.alert("Error loading",arguments[3]);
41994         } 
41995         /*
41996         try {
41997             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41998                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41999             }   
42000         } catch(e) {
42001             
42002         }
42003         */
42004     
42005         
42006         
42007         this.el.unmask(this.removeMask);
42008     },
42009     // private
42010     onLoad : function()
42011     {
42012         this.el.unmask(this.removeMask);
42013     },
42014
42015     // private
42016     onBeforeLoad : function(){
42017         if(!this.disabled){
42018             this.el.mask(this.msg, this.msgCls);
42019         }
42020     },
42021
42022     // private
42023     destroy : function(){
42024         if(this.store){
42025             this.store.un('beforeload', this.onBeforeLoad, this);
42026             this.store.un('load', this.onLoad, this);
42027             this.store.un('loadexception', this.onLoadException, this);
42028         }else{
42029             var um = this.el.getUpdateManager();
42030             um.un('beforeupdate', this.onBeforeLoad, this);
42031             um.un('update', this.onLoad, this);
42032             um.un('failure', this.onLoad, this);
42033         }
42034     }
42035 };/*
42036  * Based on:
42037  * Ext JS Library 1.1.1
42038  * Copyright(c) 2006-2007, Ext JS, LLC.
42039  *
42040  * Originally Released Under LGPL - original licence link has changed is not relivant.
42041  *
42042  * Fork - LGPL
42043  * <script type="text/javascript">
42044  */
42045
42046
42047 /**
42048  * @class Roo.XTemplate
42049  * @extends Roo.Template
42050  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42051 <pre><code>
42052 var t = new Roo.XTemplate(
42053         '&lt;select name="{name}"&gt;',
42054                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42055         '&lt;/select&gt;'
42056 );
42057  
42058 // then append, applying the master template values
42059  </code></pre>
42060  *
42061  * Supported features:
42062  *
42063  *  Tags:
42064
42065 <pre><code>
42066       {a_variable} - output encoded.
42067       {a_variable.format:("Y-m-d")} - call a method on the variable
42068       {a_variable:raw} - unencoded output
42069       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42070       {a_variable:this.method_on_template(...)} - call a method on the template object.
42071  
42072 </code></pre>
42073  *  The tpl tag:
42074 <pre><code>
42075         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42076         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42077         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42078         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42079   
42080         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42081         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42082 </code></pre>
42083  *      
42084  */
42085 Roo.XTemplate = function()
42086 {
42087     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42088     if (this.html) {
42089         this.compile();
42090     }
42091 };
42092
42093
42094 Roo.extend(Roo.XTemplate, Roo.Template, {
42095
42096     /**
42097      * The various sub templates
42098      */
42099     tpls : false,
42100     /**
42101      *
42102      * basic tag replacing syntax
42103      * WORD:WORD()
42104      *
42105      * // you can fake an object call by doing this
42106      *  x.t:(test,tesT) 
42107      * 
42108      */
42109     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42110
42111     /**
42112      * compile the template
42113      *
42114      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42115      *
42116      */
42117     compile: function()
42118     {
42119         var s = this.html;
42120      
42121         s = ['<tpl>', s, '</tpl>'].join('');
42122     
42123         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42124             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42125             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42126             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42127             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42128             m,
42129             id     = 0,
42130             tpls   = [];
42131     
42132         while(true == !!(m = s.match(re))){
42133             var forMatch   = m[0].match(nameRe),
42134                 ifMatch   = m[0].match(ifRe),
42135                 execMatch   = m[0].match(execRe),
42136                 namedMatch   = m[0].match(namedRe),
42137                 
42138                 exp  = null, 
42139                 fn   = null,
42140                 exec = null,
42141                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42142                 
42143             if (ifMatch) {
42144                 // if - puts fn into test..
42145                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42146                 if(exp){
42147                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42148                 }
42149             }
42150             
42151             if (execMatch) {
42152                 // exec - calls a function... returns empty if true is  returned.
42153                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42154                 if(exp){
42155                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42156                 }
42157             }
42158             
42159             
42160             if (name) {
42161                 // for = 
42162                 switch(name){
42163                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42164                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42165                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42166                 }
42167             }
42168             var uid = namedMatch ? namedMatch[1] : id;
42169             
42170             
42171             tpls.push({
42172                 id:     namedMatch ? namedMatch[1] : id,
42173                 target: name,
42174                 exec:   exec,
42175                 test:   fn,
42176                 body:   m[1] || ''
42177             });
42178             if (namedMatch) {
42179                 s = s.replace(m[0], '');
42180             } else { 
42181                 s = s.replace(m[0], '{xtpl'+ id + '}');
42182             }
42183             ++id;
42184         }
42185         this.tpls = [];
42186         for(var i = tpls.length-1; i >= 0; --i){
42187             this.compileTpl(tpls[i]);
42188             this.tpls[tpls[i].id] = tpls[i];
42189         }
42190         this.master = tpls[tpls.length-1];
42191         return this;
42192     },
42193     /**
42194      * same as applyTemplate, except it's done to one of the subTemplates
42195      * when using named templates, you can do:
42196      *
42197      * var str = pl.applySubTemplate('your-name', values);
42198      *
42199      * 
42200      * @param {Number} id of the template
42201      * @param {Object} values to apply to template
42202      * @param {Object} parent (normaly the instance of this object)
42203      */
42204     applySubTemplate : function(id, values, parent)
42205     {
42206         
42207         
42208         var t = this.tpls[id];
42209         
42210         
42211         try { 
42212             if(t.test && !t.test.call(this, values, parent)){
42213                 return '';
42214             }
42215         } catch(e) {
42216             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42217             Roo.log(e.toString());
42218             Roo.log(t.test);
42219             return ''
42220         }
42221         try { 
42222             
42223             if(t.exec && t.exec.call(this, values, parent)){
42224                 return '';
42225             }
42226         } catch(e) {
42227             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42228             Roo.log(e.toString());
42229             Roo.log(t.exec);
42230             return ''
42231         }
42232         try {
42233             var vs = t.target ? t.target.call(this, values, parent) : values;
42234             parent = t.target ? values : parent;
42235             if(t.target && vs instanceof Array){
42236                 var buf = [];
42237                 for(var i = 0, len = vs.length; i < len; i++){
42238                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42239                 }
42240                 return buf.join('');
42241             }
42242             return t.compiled.call(this, vs, parent);
42243         } catch (e) {
42244             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42245             Roo.log(e.toString());
42246             Roo.log(t.compiled);
42247             return '';
42248         }
42249     },
42250
42251     compileTpl : function(tpl)
42252     {
42253         var fm = Roo.util.Format;
42254         var useF = this.disableFormats !== true;
42255         var sep = Roo.isGecko ? "+" : ",";
42256         var undef = function(str) {
42257             Roo.log("Property not found :"  + str);
42258             return '';
42259         };
42260         
42261         var fn = function(m, name, format, args)
42262         {
42263             //Roo.log(arguments);
42264             args = args ? args.replace(/\\'/g,"'") : args;
42265             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42266             if (typeof(format) == 'undefined') {
42267                 format= 'htmlEncode';
42268             }
42269             if (format == 'raw' ) {
42270                 format = false;
42271             }
42272             
42273             if(name.substr(0, 4) == 'xtpl'){
42274                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42275             }
42276             
42277             // build an array of options to determine if value is undefined..
42278             
42279             // basically get 'xxxx.yyyy' then do
42280             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42281             //    (function () { Roo.log("Property not found"); return ''; })() :
42282             //    ......
42283             
42284             var udef_ar = [];
42285             var lookfor = '';
42286             Roo.each(name.split('.'), function(st) {
42287                 lookfor += (lookfor.length ? '.': '') + st;
42288                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42289             });
42290             
42291             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42292             
42293             
42294             if(format && useF){
42295                 
42296                 args = args ? ',' + args : "";
42297                  
42298                 if(format.substr(0, 5) != "this."){
42299                     format = "fm." + format + '(';
42300                 }else{
42301                     format = 'this.call("'+ format.substr(5) + '", ';
42302                     args = ", values";
42303                 }
42304                 
42305                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42306             }
42307              
42308             if (args.length) {
42309                 // called with xxyx.yuu:(test,test)
42310                 // change to ()
42311                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42312             }
42313             // raw.. - :raw modifier..
42314             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42315             
42316         };
42317         var body;
42318         // branched to use + in gecko and [].join() in others
42319         if(Roo.isGecko){
42320             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42321                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42322                     "';};};";
42323         }else{
42324             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42325             body.push(tpl.body.replace(/(\r\n|\n)/g,
42326                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42327             body.push("'].join('');};};");
42328             body = body.join('');
42329         }
42330         
42331         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42332        
42333         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42334         eval(body);
42335         
42336         return this;
42337     },
42338
42339     applyTemplate : function(values){
42340         return this.master.compiled.call(this, values, {});
42341         //var s = this.subs;
42342     },
42343
42344     apply : function(){
42345         return this.applyTemplate.apply(this, arguments);
42346     }
42347
42348  });
42349
42350 Roo.XTemplate.from = function(el){
42351     el = Roo.getDom(el);
42352     return new Roo.XTemplate(el.value || el.innerHTML);
42353 };