roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 Roo.log('run here??????????????');
3638                 Roo.log(status);
3639                 this.proxy.setStatus(status);
3640             }
3641
3642             if(this.afterDragOver){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * while the dragged item is over the drop target by providing an implementation.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOver
3650                  */
3651                 this.afterDragOver(target, e, id);
3652             }
3653         }
3654     },
3655
3656     /**
3657      * An empty function by default, but provided so that you can perform a custom action
3658      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3659      * @param {Roo.dd.DragDrop} target The drop target
3660      * @param {Event} e The event object
3661      * @param {String} id The id of the dragged element
3662      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3663      */
3664     beforeDragOver : function(target, e, id){
3665         return true;
3666     },
3667
3668     // private
3669     onDragOut : function(e, id){
3670         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3671         if(this.beforeDragOut(target, e, id) !== false){
3672             if(target.isNotifyTarget){
3673                 target.notifyOut(this, e, this.dragData);
3674             }
3675             this.proxy.reset();
3676             if(this.afterDragOut){
3677                 /**
3678                  * An empty function by default, but provided so that you can perform a custom action
3679                  * after the dragged item is dragged out of the target without dropping.
3680                  * @param {Roo.dd.DragDrop} target The drop target
3681                  * @param {Event} e The event object
3682                  * @param {String} id The id of the dragged element
3683                  * @method afterDragOut
3684                  */
3685                 this.afterDragOut(target, e, id);
3686             }
3687         }
3688         this.cachedTarget = null;
3689     },
3690
3691     /**
3692      * An empty function by default, but provided so that you can perform a custom action before the dragged
3693      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3694      * @param {Roo.dd.DragDrop} target The drop target
3695      * @param {Event} e The event object
3696      * @param {String} id The id of the dragged element
3697      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3698      */
3699     beforeDragOut : function(target, e, id){
3700         return true;
3701     },
3702     
3703     // private
3704     onDragDrop : function(e, id){
3705         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3706         if(this.beforeDragDrop(target, e, id) !== false){
3707             if(target.isNotifyTarget){
3708                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3709                     this.onValidDrop(target, e, id);
3710                 }else{
3711                     this.onInvalidDrop(target, e, id);
3712                 }
3713             }else{
3714                 this.onValidDrop(target, e, id);
3715             }
3716             
3717             if(this.afterDragDrop){
3718                 /**
3719                  * An empty function by default, but provided so that you can perform a custom action
3720                  * after a valid drag drop has occurred by providing an implementation.
3721                  * @param {Roo.dd.DragDrop} target The drop target
3722                  * @param {Event} e The event object
3723                  * @param {String} id The id of the dropped element
3724                  * @method afterDragDrop
3725                  */
3726                 this.afterDragDrop(target, e, id);
3727             }
3728         }
3729         delete this.cachedTarget;
3730     },
3731
3732     /**
3733      * An empty function by default, but provided so that you can perform a custom action before the dragged
3734      * item is dropped onto the target and optionally cancel the onDragDrop.
3735      * @param {Roo.dd.DragDrop} target The drop target
3736      * @param {Event} e The event object
3737      * @param {String} id The id of the dragged element
3738      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3739      */
3740     beforeDragDrop : function(target, e, id){
3741         return true;
3742     },
3743
3744     // private
3745     onValidDrop : function(target, e, id){
3746         this.hideProxy();
3747         if(this.afterValidDrop){
3748             /**
3749              * An empty function by default, but provided so that you can perform a custom action
3750              * after a valid drop has occurred by providing an implementation.
3751              * @param {Object} target The target DD 
3752              * @param {Event} e The event object
3753              * @param {String} id The id of the dropped element
3754              * @method afterInvalidDrop
3755              */
3756             this.afterValidDrop(target, e, id);
3757         }
3758     },
3759
3760     // private
3761     getRepairXY : function(e, data){
3762         return this.el.getXY();  
3763     },
3764
3765     // private
3766     onInvalidDrop : function(target, e, id){
3767         this.beforeInvalidDrop(target, e, id);
3768         if(this.cachedTarget){
3769             if(this.cachedTarget.isNotifyTarget){
3770                 this.cachedTarget.notifyOut(this, e, this.dragData);
3771             }
3772             this.cacheTarget = null;
3773         }
3774         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3775
3776         if(this.afterInvalidDrop){
3777             /**
3778              * An empty function by default, but provided so that you can perform a custom action
3779              * after an invalid drop has occurred by providing an implementation.
3780              * @param {Event} e The event object
3781              * @param {String} id The id of the dropped element
3782              * @method afterInvalidDrop
3783              */
3784             this.afterInvalidDrop(e, id);
3785         }
3786     },
3787
3788     // private
3789     afterRepair : function(){
3790         if(Roo.enableFx){
3791             this.el.highlight(this.hlColor || "c3daf9");
3792         }
3793         this.dragging = false;
3794     },
3795
3796     /**
3797      * An empty function by default, but provided so that you can perform a custom action after an invalid
3798      * drop has occurred.
3799      * @param {Roo.dd.DragDrop} target The drop target
3800      * @param {Event} e The event object
3801      * @param {String} id The id of the dragged element
3802      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3803      */
3804     beforeInvalidDrop : function(target, e, id){
3805         return true;
3806     },
3807
3808     // private
3809     handleMouseDown : function(e){
3810         if(this.dragging) {
3811             return;
3812         }
3813         var data = this.getDragData(e);
3814         if(data && this.onBeforeDrag(data, e) !== false){
3815             this.dragData = data;
3816             this.proxy.stop();
3817             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3818         } 
3819     },
3820
3821     /**
3822      * An empty function by default, but provided so that you can perform a custom action before the initial
3823      * drag event begins and optionally cancel it.
3824      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3825      * @param {Event} e The event object
3826      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3827      */
3828     onBeforeDrag : function(data, e){
3829         return true;
3830     },
3831
3832     /**
3833      * An empty function by default, but provided so that you can perform a custom action once the initial
3834      * drag event has begun.  The drag cannot be canceled from this function.
3835      * @param {Number} x The x position of the click on the dragged object
3836      * @param {Number} y The y position of the click on the dragged object
3837      */
3838     onStartDrag : Roo.emptyFn,
3839
3840     // private - YUI override
3841     startDrag : function(x, y){
3842         this.proxy.reset();
3843         this.dragging = true;
3844         this.proxy.update("");
3845         this.onInitDrag(x, y);
3846         this.proxy.show();
3847     },
3848
3849     // private
3850     onInitDrag : function(x, y){
3851         var clone = this.el.dom.cloneNode(true);
3852         clone.id = Roo.id(); // prevent duplicate ids
3853         this.proxy.update(clone);
3854         this.onStartDrag(x, y);
3855         return true;
3856     },
3857
3858     /**
3859      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3860      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3861      */
3862     getProxy : function(){
3863         return this.proxy;  
3864     },
3865
3866     /**
3867      * Hides the drag source's {@link Roo.dd.StatusProxy}
3868      */
3869     hideProxy : function(){
3870         this.proxy.hide();  
3871         this.proxy.reset(true);
3872         this.dragging = false;
3873     },
3874
3875     // private
3876     triggerCacheRefresh : function(){
3877         Roo.dd.DDM.refreshCache(this.groups);
3878     },
3879
3880     // private - override to prevent hiding
3881     b4EndDrag: function(e) {
3882     },
3883
3884     // private - override to prevent moving
3885     endDrag : function(e){
3886         this.onEndDrag(this.dragData, e);
3887     },
3888
3889     // private
3890     onEndDrag : function(data, e){
3891     },
3892     
3893     // private - pin to cursor
3894     autoOffset : function(x, y) {
3895         this.setDelta(-12, -20);
3896     }    
3897 });/*
3898  * Based on:
3899  * Ext JS Library 1.1.1
3900  * Copyright(c) 2006-2007, Ext JS, LLC.
3901  *
3902  * Originally Released Under LGPL - original licence link has changed is not relivant.
3903  *
3904  * Fork - LGPL
3905  * <script type="text/javascript">
3906  */
3907
3908
3909 /**
3910  * @class Roo.dd.DropTarget
3911  * @extends Roo.dd.DDTarget
3912  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3913  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3914  * @constructor
3915  * @param {String/HTMLElement/Element} el The container element
3916  * @param {Object} config
3917  */
3918 Roo.dd.DropTarget = function(el, config){
3919     this.el = Roo.get(el);
3920     
3921     var listeners = false; ;
3922     if (config && config.listeners) {
3923         listeners= config.listeners;
3924         delete config.listeners;
3925     }
3926     Roo.apply(this, config);
3927     
3928     if(this.containerScroll){
3929         Roo.dd.ScrollManager.register(this.el);
3930     }
3931     this.addEvents( {
3932          /**
3933          * @scope Roo.dd.DropTarget
3934          */
3935          
3936          /**
3937          * @event enter
3938          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3939          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3940          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3941          * 
3942          * IMPORTANT : it should set this.overClass and this.dropAllowed
3943          * 
3944          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3945          * @param {Event} e The event
3946          * @param {Object} data An object containing arbitrary data supplied by the drag source
3947          */
3948         "enter" : true,
3949         
3950          /**
3951          * @event over
3952          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3953          * This method will be called on every mouse movement while the drag source is over the drop target.
3954          * This default implementation simply returns the dropAllowed config value.
3955          * 
3956          * IMPORTANT : it should set this.dropAllowed
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961          
3962          */
3963         "over" : true,
3964         /**
3965          * @event out
3966          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3967          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3968          * overClass (if any) from the drop element.
3969          * 
3970          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971          * @param {Event} e The event
3972          * @param {Object} data An object containing arbitrary data supplied by the drag source
3973          */
3974          "out" : true,
3975          
3976         /**
3977          * @event drop
3978          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3979          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3980          * implementation that does something to process the drop event and returns true so that the drag source's
3981          * repair action does not run.
3982          * 
3983          * IMPORTANT : it should set this.success
3984          * 
3985          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3986          * @param {Event} e The event
3987          * @param {Object} data An object containing arbitrary data supplied by the drag source
3988         */
3989          "drop" : true
3990     });
3991             
3992      
3993     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3994         this.el.dom, 
3995         this.ddGroup || this.group,
3996         {
3997             isTarget: true,
3998             listeners : listeners || {} 
3999            
4000         
4001         }
4002     );
4003
4004 };
4005
4006 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4007     /**
4008      * @cfg {String} overClass
4009      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4010      */
4011      /**
4012      * @cfg {String} ddGroup
4013      * The drag drop group to handle drop events for
4014      */
4015      
4016     /**
4017      * @cfg {String} dropAllowed
4018      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4019      */
4020     dropAllowed : "x-dd-drop-ok",
4021     /**
4022      * @cfg {String} dropNotAllowed
4023      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4024      */
4025     dropNotAllowed : "x-dd-drop-nodrop",
4026     /**
4027      * @cfg {boolean} success
4028      * set this after drop listener.. 
4029      */
4030     success : false,
4031     /**
4032      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4033      * if the drop point is valid for over/enter..
4034      */
4035     valid : false,
4036     // private
4037     isTarget : true,
4038
4039     // private
4040     isNotifyTarget : true,
4041     
4042     /**
4043      * @hide
4044      */
4045     notifyEnter : function(dd, e, data)
4046     {
4047         this.valid = true;
4048         this.fireEvent('enter', dd, e, data);
4049         if(this.overClass){
4050             this.el.addClass(this.overClass);
4051         }
4052         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4053             this.valid ? this.dropAllowed : this.dropNotAllowed
4054         );
4055     },
4056
4057     /**
4058      * @hide
4059      */
4060     notifyOver : function(dd, e, data)
4061     {
4062         this.valid = true;
4063         this.fireEvent('over', dd, e, data);
4064         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4065             this.valid ? this.dropAllowed : this.dropNotAllowed
4066         );
4067     },
4068
4069     /**
4070      * @hide
4071      */
4072     notifyOut : function(dd, e, data)
4073     {
4074         this.fireEvent('out', dd, e, data);
4075         if(this.overClass){
4076             this.el.removeClass(this.overClass);
4077         }
4078     },
4079
4080     /**
4081      * @hide
4082      */
4083     notifyDrop : function(dd, e, data)
4084     {
4085         this.success = false;
4086         this.fireEvent('drop', dd, e, data);
4087         return this.success;
4088     }
4089 });/*
4090  * Based on:
4091  * Ext JS Library 1.1.1
4092  * Copyright(c) 2006-2007, Ext JS, LLC.
4093  *
4094  * Originally Released Under LGPL - original licence link has changed is not relivant.
4095  *
4096  * Fork - LGPL
4097  * <script type="text/javascript">
4098  */
4099
4100
4101 /**
4102  * @class Roo.dd.DragZone
4103  * @extends Roo.dd.DragSource
4104  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4105  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4106  * @constructor
4107  * @param {String/HTMLElement/Element} el The container element
4108  * @param {Object} config
4109  */
4110 Roo.dd.DragZone = function(el, config){
4111     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4112     if(this.containerScroll){
4113         Roo.dd.ScrollManager.register(this.el);
4114     }
4115 };
4116
4117 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4118     /**
4119      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4120      * for auto scrolling during drag operations.
4121      */
4122     /**
4123      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4124      * method after a failed drop (defaults to "c3daf9" - light blue)
4125      */
4126
4127     /**
4128      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4129      * for a valid target to drag based on the mouse down. Override this method
4130      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4131      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4132      * @param {EventObject} e The mouse down event
4133      * @return {Object} The dragData
4134      */
4135     getDragData : function(e){
4136         return Roo.dd.Registry.getHandleFromEvent(e);
4137     },
4138     
4139     /**
4140      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4141      * this.dragData.ddel
4142      * @param {Number} x The x position of the click on the dragged object
4143      * @param {Number} y The y position of the click on the dragged object
4144      * @return {Boolean} true to continue the drag, false to cancel
4145      */
4146     onInitDrag : function(x, y){
4147         this.proxy.update(this.dragData.ddel.cloneNode(true));
4148         this.onStartDrag(x, y);
4149         return true;
4150     },
4151     
4152     /**
4153      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4154      */
4155     afterRepair : function(){
4156         if(Roo.enableFx){
4157             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4158         }
4159         this.dragging = false;
4160     },
4161
4162     /**
4163      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4164      * the XY of this.dragData.ddel
4165      * @param {EventObject} e The mouse up event
4166      * @return {Array} The xy location (e.g. [100, 200])
4167      */
4168     getRepairXY : function(e){
4169         return Roo.Element.fly(this.dragData.ddel).getXY();  
4170     }
4171 });/*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181 /**
4182  * @class Roo.dd.DropZone
4183  * @extends Roo.dd.DropTarget
4184  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4185  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4186  * @constructor
4187  * @param {String/HTMLElement/Element} el The container element
4188  * @param {Object} config
4189  */
4190 Roo.dd.DropZone = function(el, config){
4191     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4192 };
4193
4194 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4195     /**
4196      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4197      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4198      * provide your own custom lookup.
4199      * @param {Event} e The event
4200      * @return {Object} data The custom data
4201      */
4202     getTargetFromEvent : function(e){
4203         return Roo.dd.Registry.getTargetFromEvent(e);
4204     },
4205
4206     /**
4207      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4208      * that it has registered.  This method has no default implementation and should be overridden to provide
4209      * node-specific processing if necessary.
4210      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4211      * {@link #getTargetFromEvent} for this node)
4212      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4213      * @param {Event} e The event
4214      * @param {Object} data An object containing arbitrary data supplied by the drag source
4215      */
4216     onNodeEnter : function(n, dd, e, data){
4217         
4218     },
4219
4220     /**
4221      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4222      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4223      * overridden to provide the proper feedback.
4224      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4225      * {@link #getTargetFromEvent} for this node)
4226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4227      * @param {Event} e The event
4228      * @param {Object} data An object containing arbitrary data supplied by the drag source
4229      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4230      * underlying {@link Roo.dd.StatusProxy} can be updated
4231      */
4232     onNodeOver : function(n, dd, e, data){
4233         return this.dropAllowed;
4234     },
4235
4236     /**
4237      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4238      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4239      * node-specific processing if necessary.
4240      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4241      * {@link #getTargetFromEvent} for this node)
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      */
4246     onNodeOut : function(n, dd, e, data){
4247         
4248     },
4249
4250     /**
4251      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4252      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4253      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4254      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4255      * {@link #getTargetFromEvent} for this node)
4256      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4257      * @param {Event} e The event
4258      * @param {Object} data An object containing arbitrary data supplied by the drag source
4259      * @return {Boolean} True if the drop was valid, else false
4260      */
4261     onNodeDrop : function(n, dd, e, data){
4262         return false;
4263     },
4264
4265     /**
4266      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4267      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4268      * it should be overridden to provide the proper feedback if necessary.
4269      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4270      * @param {Event} e The event
4271      * @param {Object} data An object containing arbitrary data supplied by the drag source
4272      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4273      * underlying {@link Roo.dd.StatusProxy} can be updated
4274      */
4275     onContainerOver : function(dd, e, data){
4276         return this.dropNotAllowed;
4277     },
4278
4279     /**
4280      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4281      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4282      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4283      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4284      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4285      * @param {Event} e The event
4286      * @param {Object} data An object containing arbitrary data supplied by the drag source
4287      * @return {Boolean} True if the drop was valid, else false
4288      */
4289     onContainerDrop : function(dd, e, data){
4290         return false;
4291     },
4292
4293     /**
4294      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4295      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4296      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4297      * you should override this method and provide a custom implementation.
4298      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4299      * @param {Event} e The event
4300      * @param {Object} data An object containing arbitrary data supplied by the drag source
4301      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4302      * underlying {@link Roo.dd.StatusProxy} can be updated
4303      */
4304     notifyEnter : function(dd, e, data){
4305         return this.dropNotAllowed;
4306     },
4307
4308     /**
4309      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4310      * This method will be called on every mouse movement while the drag source is over the drop zone.
4311      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4312      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4313      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4314      * registered node, it will call {@link #onContainerOver}.
4315      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4316      * @param {Event} e The event
4317      * @param {Object} data An object containing arbitrary data supplied by the drag source
4318      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4319      * underlying {@link Roo.dd.StatusProxy} can be updated
4320      */
4321     notifyOver : function(dd, e, data){
4322         var n = this.getTargetFromEvent(e);
4323         if(!n){ // not over valid drop target
4324             if(this.lastOverNode){
4325                 this.onNodeOut(this.lastOverNode, dd, e, data);
4326                 this.lastOverNode = null;
4327             }
4328             return this.onContainerOver(dd, e, data);
4329         }
4330         if(this.lastOverNode != n){
4331             if(this.lastOverNode){
4332                 this.onNodeOut(this.lastOverNode, dd, e, data);
4333             }
4334             this.onNodeEnter(n, dd, e, data);
4335             this.lastOverNode = n;
4336         }
4337         return this.onNodeOver(n, dd, e, data);
4338     },
4339
4340     /**
4341      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4342      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4343      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4344      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4345      * @param {Event} e The event
4346      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4347      */
4348     notifyOut : function(dd, e, data){
4349         if(this.lastOverNode){
4350             this.onNodeOut(this.lastOverNode, dd, e, data);
4351             this.lastOverNode = null;
4352         }
4353     },
4354
4355     /**
4356      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4357      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4358      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4359      * otherwise it will call {@link #onContainerDrop}.
4360      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4361      * @param {Event} e The event
4362      * @param {Object} data An object containing arbitrary data supplied by the drag source
4363      * @return {Boolean} True if the drop was valid, else false
4364      */
4365     notifyDrop : function(dd, e, data){
4366         if(this.lastOverNode){
4367             this.onNodeOut(this.lastOverNode, dd, e, data);
4368             this.lastOverNode = null;
4369         }
4370         var n = this.getTargetFromEvent(e);
4371         return n ?
4372             this.onNodeDrop(n, dd, e, data) :
4373             this.onContainerDrop(dd, e, data);
4374     },
4375
4376     // private
4377     triggerCacheRefresh : function(){
4378         Roo.dd.DDM.refreshCache(this.groups);
4379     }  
4380 });/*
4381  * Based on:
4382  * Ext JS Library 1.1.1
4383  * Copyright(c) 2006-2007, Ext JS, LLC.
4384  *
4385  * Originally Released Under LGPL - original licence link has changed is not relivant.
4386  *
4387  * Fork - LGPL
4388  * <script type="text/javascript">
4389  */
4390
4391
4392 /**
4393  * @class Roo.data.SortTypes
4394  * @singleton
4395  * Defines the default sorting (casting?) comparison functions used when sorting data.
4396  */
4397 Roo.data.SortTypes = {
4398     /**
4399      * Default sort that does nothing
4400      * @param {Mixed} s The value being converted
4401      * @return {Mixed} The comparison value
4402      */
4403     none : function(s){
4404         return s;
4405     },
4406     
4407     /**
4408      * The regular expression used to strip tags
4409      * @type {RegExp}
4410      * @property
4411      */
4412     stripTagsRE : /<\/?[^>]+>/gi,
4413     
4414     /**
4415      * Strips all HTML tags to sort on text only
4416      * @param {Mixed} s The value being converted
4417      * @return {String} The comparison value
4418      */
4419     asText : function(s){
4420         return String(s).replace(this.stripTagsRE, "");
4421     },
4422     
4423     /**
4424      * Strips all HTML tags to sort on text only - Case insensitive
4425      * @param {Mixed} s The value being converted
4426      * @return {String} The comparison value
4427      */
4428     asUCText : function(s){
4429         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4430     },
4431     
4432     /**
4433      * Case insensitive string
4434      * @param {Mixed} s The value being converted
4435      * @return {String} The comparison value
4436      */
4437     asUCString : function(s) {
4438         return String(s).toUpperCase();
4439     },
4440     
4441     /**
4442      * Date sorting
4443      * @param {Mixed} s The value being converted
4444      * @return {Number} The comparison value
4445      */
4446     asDate : function(s) {
4447         if(!s){
4448             return 0;
4449         }
4450         if(s instanceof Date){
4451             return s.getTime();
4452         }
4453         return Date.parse(String(s));
4454     },
4455     
4456     /**
4457      * Float sorting
4458      * @param {Mixed} s The value being converted
4459      * @return {Float} The comparison value
4460      */
4461     asFloat : function(s) {
4462         var val = parseFloat(String(s).replace(/,/g, ""));
4463         if(isNaN(val)) val = 0;
4464         return val;
4465     },
4466     
4467     /**
4468      * Integer sorting
4469      * @param {Mixed} s The value being converted
4470      * @return {Number} The comparison value
4471      */
4472     asInt : function(s) {
4473         var val = parseInt(String(s).replace(/,/g, ""));
4474         if(isNaN(val)) val = 0;
4475         return val;
4476     }
4477 };/*
4478  * Based on:
4479  * Ext JS Library 1.1.1
4480  * Copyright(c) 2006-2007, Ext JS, LLC.
4481  *
4482  * Originally Released Under LGPL - original licence link has changed is not relivant.
4483  *
4484  * Fork - LGPL
4485  * <script type="text/javascript">
4486  */
4487
4488 /**
4489 * @class Roo.data.Record
4490  * Instances of this class encapsulate both record <em>definition</em> information, and record
4491  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4492  * to access Records cached in an {@link Roo.data.Store} object.<br>
4493  * <p>
4494  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4495  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4496  * objects.<br>
4497  * <p>
4498  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4499  * @constructor
4500  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4501  * {@link #create}. The parameters are the same.
4502  * @param {Array} data An associative Array of data values keyed by the field name.
4503  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4504  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4505  * not specified an integer id is generated.
4506  */
4507 Roo.data.Record = function(data, id){
4508     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4509     this.data = data;
4510 };
4511
4512 /**
4513  * Generate a constructor for a specific record layout.
4514  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4515  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4516  * Each field definition object may contain the following properties: <ul>
4517  * <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,
4518  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4519  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4520  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4521  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4522  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4523  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4524  * this may be omitted.</p></li>
4525  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4526  * <ul><li>auto (Default, implies no conversion)</li>
4527  * <li>string</li>
4528  * <li>int</li>
4529  * <li>float</li>
4530  * <li>boolean</li>
4531  * <li>date</li></ul></p></li>
4532  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4533  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4534  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4535  * by the Reader into an object that will be stored in the Record. It is passed the
4536  * following parameters:<ul>
4537  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4538  * </ul></p></li>
4539  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4540  * </ul>
4541  * <br>usage:<br><pre><code>
4542 var TopicRecord = Roo.data.Record.create(
4543     {name: 'title', mapping: 'topic_title'},
4544     {name: 'author', mapping: 'username'},
4545     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4546     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4547     {name: 'lastPoster', mapping: 'user2'},
4548     {name: 'excerpt', mapping: 'post_text'}
4549 );
4550
4551 var myNewRecord = new TopicRecord({
4552     title: 'Do my job please',
4553     author: 'noobie',
4554     totalPosts: 1,
4555     lastPost: new Date(),
4556     lastPoster: 'Animal',
4557     excerpt: 'No way dude!'
4558 });
4559 myStore.add(myNewRecord);
4560 </code></pre>
4561  * @method create
4562  * @static
4563  */
4564 Roo.data.Record.create = function(o){
4565     var f = function(){
4566         f.superclass.constructor.apply(this, arguments);
4567     };
4568     Roo.extend(f, Roo.data.Record);
4569     var p = f.prototype;
4570     p.fields = new Roo.util.MixedCollection(false, function(field){
4571         return field.name;
4572     });
4573     for(var i = 0, len = o.length; i < len; i++){
4574         p.fields.add(new Roo.data.Field(o[i]));
4575     }
4576     f.getField = function(name){
4577         return p.fields.get(name);  
4578     };
4579     return f;
4580 };
4581
4582 Roo.data.Record.AUTO_ID = 1000;
4583 Roo.data.Record.EDIT = 'edit';
4584 Roo.data.Record.REJECT = 'reject';
4585 Roo.data.Record.COMMIT = 'commit';
4586
4587 Roo.data.Record.prototype = {
4588     /**
4589      * Readonly flag - true if this record has been modified.
4590      * @type Boolean
4591      */
4592     dirty : false,
4593     editing : false,
4594     error: null,
4595     modified: null,
4596
4597     // private
4598     join : function(store){
4599         this.store = store;
4600     },
4601
4602     /**
4603      * Set the named field to the specified value.
4604      * @param {String} name The name of the field to set.
4605      * @param {Object} value The value to set the field to.
4606      */
4607     set : function(name, value){
4608         if(this.data[name] == value){
4609             return;
4610         }
4611         this.dirty = true;
4612         if(!this.modified){
4613             this.modified = {};
4614         }
4615         if(typeof this.modified[name] == 'undefined'){
4616             this.modified[name] = this.data[name];
4617         }
4618         this.data[name] = value;
4619         if(!this.editing && this.store){
4620             this.store.afterEdit(this);
4621         }       
4622     },
4623
4624     /**
4625      * Get the value of the named field.
4626      * @param {String} name The name of the field to get the value of.
4627      * @return {Object} The value of the field.
4628      */
4629     get : function(name){
4630         return this.data[name]; 
4631     },
4632
4633     // private
4634     beginEdit : function(){
4635         this.editing = true;
4636         this.modified = {}; 
4637     },
4638
4639     // private
4640     cancelEdit : function(){
4641         this.editing = false;
4642         delete this.modified;
4643     },
4644
4645     // private
4646     endEdit : function(){
4647         this.editing = false;
4648         if(this.dirty && this.store){
4649             this.store.afterEdit(this);
4650         }
4651     },
4652
4653     /**
4654      * Usually called by the {@link Roo.data.Store} which owns the Record.
4655      * Rejects all changes made to the Record since either creation, or the last commit operation.
4656      * Modified fields are reverted to their original values.
4657      * <p>
4658      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4659      * of reject operations.
4660      */
4661     reject : function(){
4662         var m = this.modified;
4663         for(var n in m){
4664             if(typeof m[n] != "function"){
4665                 this.data[n] = m[n];
4666             }
4667         }
4668         this.dirty = false;
4669         delete this.modified;
4670         this.editing = false;
4671         if(this.store){
4672             this.store.afterReject(this);
4673         }
4674     },
4675
4676     /**
4677      * Usually called by the {@link Roo.data.Store} which owns the Record.
4678      * Commits all changes made to the Record since either creation, or the last commit operation.
4679      * <p>
4680      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4681      * of commit operations.
4682      */
4683     commit : function(){
4684         this.dirty = false;
4685         delete this.modified;
4686         this.editing = false;
4687         if(this.store){
4688             this.store.afterCommit(this);
4689         }
4690     },
4691
4692     // private
4693     hasError : function(){
4694         return this.error != null;
4695     },
4696
4697     // private
4698     clearError : function(){
4699         this.error = null;
4700     },
4701
4702     /**
4703      * Creates a copy of this record.
4704      * @param {String} id (optional) A new record id if you don't want to use this record's id
4705      * @return {Record}
4706      */
4707     copy : function(newId) {
4708         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4709     }
4710 };/*
4711  * Based on:
4712  * Ext JS Library 1.1.1
4713  * Copyright(c) 2006-2007, Ext JS, LLC.
4714  *
4715  * Originally Released Under LGPL - original licence link has changed is not relivant.
4716  *
4717  * Fork - LGPL
4718  * <script type="text/javascript">
4719  */
4720
4721
4722
4723 /**
4724  * @class Roo.data.Store
4725  * @extends Roo.util.Observable
4726  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4727  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4728  * <p>
4729  * 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
4730  * has no knowledge of the format of the data returned by the Proxy.<br>
4731  * <p>
4732  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4733  * instances from the data object. These records are cached and made available through accessor functions.
4734  * @constructor
4735  * Creates a new Store.
4736  * @param {Object} config A config object containing the objects needed for the Store to access data,
4737  * and read the data into Records.
4738  */
4739 Roo.data.Store = function(config){
4740     this.data = new Roo.util.MixedCollection(false);
4741     this.data.getKey = function(o){
4742         return o.id;
4743     };
4744     this.baseParams = {};
4745     // private
4746     this.paramNames = {
4747         "start" : "start",
4748         "limit" : "limit",
4749         "sort" : "sort",
4750         "dir" : "dir",
4751         "multisort" : "_multisort"
4752     };
4753
4754     if(config && config.data){
4755         this.inlineData = config.data;
4756         delete config.data;
4757     }
4758
4759     Roo.apply(this, config);
4760     
4761     if(this.reader){ // reader passed
4762         this.reader = Roo.factory(this.reader, Roo.data);
4763         this.reader.xmodule = this.xmodule || false;
4764         if(!this.recordType){
4765             this.recordType = this.reader.recordType;
4766         }
4767         if(this.reader.onMetaChange){
4768             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4769         }
4770     }
4771
4772     if(this.recordType){
4773         this.fields = this.recordType.prototype.fields;
4774     }
4775     this.modified = [];
4776
4777     this.addEvents({
4778         /**
4779          * @event datachanged
4780          * Fires when the data cache has changed, and a widget which is using this Store
4781          * as a Record cache should refresh its view.
4782          * @param {Store} this
4783          */
4784         datachanged : true,
4785         /**
4786          * @event metachange
4787          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4788          * @param {Store} this
4789          * @param {Object} meta The JSON metadata
4790          */
4791         metachange : true,
4792         /**
4793          * @event add
4794          * Fires when Records have been added to the Store
4795          * @param {Store} this
4796          * @param {Roo.data.Record[]} records The array of Records added
4797          * @param {Number} index The index at which the record(s) were added
4798          */
4799         add : true,
4800         /**
4801          * @event remove
4802          * Fires when a Record has been removed from the Store
4803          * @param {Store} this
4804          * @param {Roo.data.Record} record The Record that was removed
4805          * @param {Number} index The index at which the record was removed
4806          */
4807         remove : true,
4808         /**
4809          * @event update
4810          * Fires when a Record has been updated
4811          * @param {Store} this
4812          * @param {Roo.data.Record} record The Record that was updated
4813          * @param {String} operation The update operation being performed.  Value may be one of:
4814          * <pre><code>
4815  Roo.data.Record.EDIT
4816  Roo.data.Record.REJECT
4817  Roo.data.Record.COMMIT
4818          * </code></pre>
4819          */
4820         update : true,
4821         /**
4822          * @event clear
4823          * Fires when the data cache has been cleared.
4824          * @param {Store} this
4825          */
4826         clear : true,
4827         /**
4828          * @event beforeload
4829          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4830          * the load action will be canceled.
4831          * @param {Store} this
4832          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4833          */
4834         beforeload : true,
4835         /**
4836          * @event beforeloadadd
4837          * Fires after a new set of Records has been loaded.
4838          * @param {Store} this
4839          * @param {Roo.data.Record[]} records The Records that were loaded
4840          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4841          */
4842         beforeloadadd : true,
4843         /**
4844          * @event load
4845          * Fires after a new set of Records has been loaded, before they are added to the store.
4846          * @param {Store} this
4847          * @param {Roo.data.Record[]} records The Records that were loaded
4848          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4849          * @params {Object} return from reader
4850          */
4851         load : true,
4852         /**
4853          * @event loadexception
4854          * Fires if an exception occurs in the Proxy during loading.
4855          * Called with the signature of the Proxy's "loadexception" event.
4856          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4857          * 
4858          * @param {Proxy} 
4859          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4860          * @param {Object} load options 
4861          * @param {Object} jsonData from your request (normally this contains the Exception)
4862          */
4863         loadexception : true
4864     });
4865     
4866     if(this.proxy){
4867         this.proxy = Roo.factory(this.proxy, Roo.data);
4868         this.proxy.xmodule = this.xmodule || false;
4869         this.relayEvents(this.proxy,  ["loadexception"]);
4870     }
4871     this.sortToggle = {};
4872     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4873
4874     Roo.data.Store.superclass.constructor.call(this);
4875
4876     if(this.inlineData){
4877         this.loadData(this.inlineData);
4878         delete this.inlineData;
4879     }
4880 };
4881
4882 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4883      /**
4884     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4885     * without a remote query - used by combo/forms at present.
4886     */
4887     
4888     /**
4889     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4890     */
4891     /**
4892     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4893     */
4894     /**
4895     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4896     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4897     */
4898     /**
4899     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4900     * on any HTTP request
4901     */
4902     /**
4903     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4904     */
4905     /**
4906     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4907     */
4908     multiSort: false,
4909     /**
4910     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4911     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4912     */
4913     remoteSort : false,
4914
4915     /**
4916     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4917      * loaded or when a record is removed. (defaults to false).
4918     */
4919     pruneModifiedRecords : false,
4920
4921     // private
4922     lastOptions : null,
4923
4924     /**
4925      * Add Records to the Store and fires the add event.
4926      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4927      */
4928     add : function(records){
4929         records = [].concat(records);
4930         for(var i = 0, len = records.length; i < len; i++){
4931             records[i].join(this);
4932         }
4933         var index = this.data.length;
4934         this.data.addAll(records);
4935         this.fireEvent("add", this, records, index);
4936     },
4937
4938     /**
4939      * Remove a Record from the Store and fires the remove event.
4940      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4941      */
4942     remove : function(record){
4943         var index = this.data.indexOf(record);
4944         this.data.removeAt(index);
4945         if(this.pruneModifiedRecords){
4946             this.modified.remove(record);
4947         }
4948         this.fireEvent("remove", this, record, index);
4949     },
4950
4951     /**
4952      * Remove all Records from the Store and fires the clear event.
4953      */
4954     removeAll : function(){
4955         this.data.clear();
4956         if(this.pruneModifiedRecords){
4957             this.modified = [];
4958         }
4959         this.fireEvent("clear", this);
4960     },
4961
4962     /**
4963      * Inserts Records to the Store at the given index and fires the add event.
4964      * @param {Number} index The start index at which to insert the passed Records.
4965      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4966      */
4967     insert : function(index, records){
4968         records = [].concat(records);
4969         for(var i = 0, len = records.length; i < len; i++){
4970             this.data.insert(index, records[i]);
4971             records[i].join(this);
4972         }
4973         this.fireEvent("add", this, records, index);
4974     },
4975
4976     /**
4977      * Get the index within the cache of the passed Record.
4978      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4979      * @return {Number} The index of the passed Record. Returns -1 if not found.
4980      */
4981     indexOf : function(record){
4982         return this.data.indexOf(record);
4983     },
4984
4985     /**
4986      * Get the index within the cache of the Record with the passed id.
4987      * @param {String} id The id of the Record to find.
4988      * @return {Number} The index of the Record. Returns -1 if not found.
4989      */
4990     indexOfId : function(id){
4991         return this.data.indexOfKey(id);
4992     },
4993
4994     /**
4995      * Get the Record with the specified id.
4996      * @param {String} id The id of the Record to find.
4997      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4998      */
4999     getById : function(id){
5000         return this.data.key(id);
5001     },
5002
5003     /**
5004      * Get the Record at the specified index.
5005      * @param {Number} index The index of the Record to find.
5006      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5007      */
5008     getAt : function(index){
5009         return this.data.itemAt(index);
5010     },
5011
5012     /**
5013      * Returns a range of Records between specified indices.
5014      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5015      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5016      * @return {Roo.data.Record[]} An array of Records
5017      */
5018     getRange : function(start, end){
5019         return this.data.getRange(start, end);
5020     },
5021
5022     // private
5023     storeOptions : function(o){
5024         o = Roo.apply({}, o);
5025         delete o.callback;
5026         delete o.scope;
5027         this.lastOptions = o;
5028     },
5029
5030     /**
5031      * Loads the Record cache from the configured Proxy using the configured Reader.
5032      * <p>
5033      * If using remote paging, then the first load call must specify the <em>start</em>
5034      * and <em>limit</em> properties in the options.params property to establish the initial
5035      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5036      * <p>
5037      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5038      * and this call will return before the new data has been loaded. Perform any post-processing
5039      * in a callback function, or in a "load" event handler.</strong>
5040      * <p>
5041      * @param {Object} options An object containing properties which control loading options:<ul>
5042      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5043      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5044      * passed the following arguments:<ul>
5045      * <li>r : Roo.data.Record[]</li>
5046      * <li>options: Options object from the load call</li>
5047      * <li>success: Boolean success indicator</li></ul></li>
5048      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5049      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5050      * </ul>
5051      */
5052     load : function(options){
5053         options = options || {};
5054         if(this.fireEvent("beforeload", this, options) !== false){
5055             this.storeOptions(options);
5056             var p = Roo.apply(options.params || {}, this.baseParams);
5057             // if meta was not loaded from remote source.. try requesting it.
5058             if (!this.reader.metaFromRemote) {
5059                 p._requestMeta = 1;
5060             }
5061             if(this.sortInfo && this.remoteSort){
5062                 var pn = this.paramNames;
5063                 p[pn["sort"]] = this.sortInfo.field;
5064                 p[pn["dir"]] = this.sortInfo.direction;
5065             }
5066             if (this.multiSort) {
5067                 var pn = this.paramNames;
5068                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5069             }
5070             
5071             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5072         }
5073     },
5074
5075     /**
5076      * Reloads the Record cache from the configured Proxy using the configured Reader and
5077      * the options from the last load operation performed.
5078      * @param {Object} options (optional) An object containing properties which may override the options
5079      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5080      * the most recently used options are reused).
5081      */
5082     reload : function(options){
5083         this.load(Roo.applyIf(options||{}, this.lastOptions));
5084     },
5085
5086     // private
5087     // Called as a callback by the Reader during a load operation.
5088     loadRecords : function(o, options, success){
5089         if(!o || success === false){
5090             if(success !== false){
5091                 this.fireEvent("load", this, [], options, o);
5092             }
5093             if(options.callback){
5094                 options.callback.call(options.scope || this, [], options, false);
5095             }
5096             return;
5097         }
5098         // if data returned failure - throw an exception.
5099         if (o.success === false) {
5100             // show a message if no listener is registered.
5101             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5102                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5103             }
5104             // loadmask wil be hooked into this..
5105             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5106             return;
5107         }
5108         var r = o.records, t = o.totalRecords || r.length;
5109         
5110         this.fireEvent("beforeloadadd", this, r, options, o);
5111         
5112         if(!options || options.add !== true){
5113             if(this.pruneModifiedRecords){
5114                 this.modified = [];
5115             }
5116             for(var i = 0, len = r.length; i < len; i++){
5117                 r[i].join(this);
5118             }
5119             if(this.snapshot){
5120                 this.data = this.snapshot;
5121                 delete this.snapshot;
5122             }
5123             this.data.clear();
5124             this.data.addAll(r);
5125             this.totalLength = t;
5126             this.applySort();
5127             this.fireEvent("datachanged", this);
5128         }else{
5129             this.totalLength = Math.max(t, this.data.length+r.length);
5130             this.add(r);
5131         }
5132         this.fireEvent("load", this, r, options, o);
5133         if(options.callback){
5134             options.callback.call(options.scope || this, r, options, true);
5135         }
5136     },
5137
5138
5139     /**
5140      * Loads data from a passed data block. A Reader which understands the format of the data
5141      * must have been configured in the constructor.
5142      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5143      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5144      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5145      */
5146     loadData : function(o, append){
5147         var r = this.reader.readRecords(o);
5148         this.loadRecords(r, {add: append}, true);
5149     },
5150
5151     /**
5152      * Gets the number of cached records.
5153      * <p>
5154      * <em>If using paging, this may not be the total size of the dataset. If the data object
5155      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5156      * the data set size</em>
5157      */
5158     getCount : function(){
5159         return this.data.length || 0;
5160     },
5161
5162     /**
5163      * Gets the total number of records in the dataset as returned by the server.
5164      * <p>
5165      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5166      * the dataset size</em>
5167      */
5168     getTotalCount : function(){
5169         return this.totalLength || 0;
5170     },
5171
5172     /**
5173      * Returns the sort state of the Store as an object with two properties:
5174      * <pre><code>
5175  field {String} The name of the field by which the Records are sorted
5176  direction {String} The sort order, "ASC" or "DESC"
5177      * </code></pre>
5178      */
5179     getSortState : function(){
5180         return this.sortInfo;
5181     },
5182
5183     // private
5184     applySort : function(){
5185         if(this.sortInfo && !this.remoteSort){
5186             var s = this.sortInfo, f = s.field;
5187             var st = this.fields.get(f).sortType;
5188             var fn = function(r1, r2){
5189                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5190                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5191             };
5192             this.data.sort(s.direction, fn);
5193             if(this.snapshot && this.snapshot != this.data){
5194                 this.snapshot.sort(s.direction, fn);
5195             }
5196         }
5197     },
5198
5199     /**
5200      * Sets the default sort column and order to be used by the next load operation.
5201      * @param {String} fieldName The name of the field to sort by.
5202      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5203      */
5204     setDefaultSort : function(field, dir){
5205         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5206     },
5207
5208     /**
5209      * Sort the Records.
5210      * If remote sorting is used, the sort is performed on the server, and the cache is
5211      * reloaded. If local sorting is used, the cache is sorted internally.
5212      * @param {String} fieldName The name of the field to sort by.
5213      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5214      */
5215     sort : function(fieldName, dir){
5216         var f = this.fields.get(fieldName);
5217         if(!dir){
5218             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5219             
5220             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5221                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5222             }else{
5223                 dir = f.sortDir;
5224             }
5225         }
5226         this.sortToggle[f.name] = dir;
5227         this.sortInfo = {field: f.name, direction: dir};
5228         if(!this.remoteSort){
5229             this.applySort();
5230             this.fireEvent("datachanged", this);
5231         }else{
5232             this.load(this.lastOptions);
5233         }
5234     },
5235
5236     /**
5237      * Calls the specified function for each of the Records in the cache.
5238      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5239      * Returning <em>false</em> aborts and exits the iteration.
5240      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5241      */
5242     each : function(fn, scope){
5243         this.data.each(fn, scope);
5244     },
5245
5246     /**
5247      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5248      * (e.g., during paging).
5249      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5250      */
5251     getModifiedRecords : function(){
5252         return this.modified;
5253     },
5254
5255     // private
5256     createFilterFn : function(property, value, anyMatch){
5257         if(!value.exec){ // not a regex
5258             value = String(value);
5259             if(value.length == 0){
5260                 return false;
5261             }
5262             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5263         }
5264         return function(r){
5265             return value.test(r.data[property]);
5266         };
5267     },
5268
5269     /**
5270      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5271      * @param {String} property A field on your records
5272      * @param {Number} start The record index to start at (defaults to 0)
5273      * @param {Number} end The last record index to include (defaults to length - 1)
5274      * @return {Number} The sum
5275      */
5276     sum : function(property, start, end){
5277         var rs = this.data.items, v = 0;
5278         start = start || 0;
5279         end = (end || end === 0) ? end : rs.length-1;
5280
5281         for(var i = start; i <= end; i++){
5282             v += (rs[i].data[property] || 0);
5283         }
5284         return v;
5285     },
5286
5287     /**
5288      * Filter the records by a specified property.
5289      * @param {String} field A field on your records
5290      * @param {String/RegExp} value Either a string that the field
5291      * should start with or a RegExp to test against the field
5292      * @param {Boolean} anyMatch True to match any part not just the beginning
5293      */
5294     filter : function(property, value, anyMatch){
5295         var fn = this.createFilterFn(property, value, anyMatch);
5296         return fn ? this.filterBy(fn) : this.clearFilter();
5297     },
5298
5299     /**
5300      * Filter by a function. The specified function will be called with each
5301      * record in this data source. If the function returns true the record is included,
5302      * otherwise it is filtered.
5303      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5304      * @param {Object} scope (optional) The scope of the function (defaults to this)
5305      */
5306     filterBy : function(fn, scope){
5307         this.snapshot = this.snapshot || this.data;
5308         this.data = this.queryBy(fn, scope||this);
5309         this.fireEvent("datachanged", this);
5310     },
5311
5312     /**
5313      * Query the records by a specified property.
5314      * @param {String} field A field on your records
5315      * @param {String/RegExp} value Either a string that the field
5316      * should start with or a RegExp to test against the field
5317      * @param {Boolean} anyMatch True to match any part not just the beginning
5318      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5319      */
5320     query : function(property, value, anyMatch){
5321         var fn = this.createFilterFn(property, value, anyMatch);
5322         return fn ? this.queryBy(fn) : this.data.clone();
5323     },
5324
5325     /**
5326      * Query by a function. The specified function will be called with each
5327      * record in this data source. If the function returns true the record is included
5328      * in the results.
5329      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5330      * @param {Object} scope (optional) The scope of the function (defaults to this)
5331       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5332      **/
5333     queryBy : function(fn, scope){
5334         var data = this.snapshot || this.data;
5335         return data.filterBy(fn, scope||this);
5336     },
5337
5338     /**
5339      * Collects unique values for a particular dataIndex from this store.
5340      * @param {String} dataIndex The property to collect
5341      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5342      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5343      * @return {Array} An array of the unique values
5344      **/
5345     collect : function(dataIndex, allowNull, bypassFilter){
5346         var d = (bypassFilter === true && this.snapshot) ?
5347                 this.snapshot.items : this.data.items;
5348         var v, sv, r = [], l = {};
5349         for(var i = 0, len = d.length; i < len; i++){
5350             v = d[i].data[dataIndex];
5351             sv = String(v);
5352             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5353                 l[sv] = true;
5354                 r[r.length] = v;
5355             }
5356         }
5357         return r;
5358     },
5359
5360     /**
5361      * Revert to a view of the Record cache with no filtering applied.
5362      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5363      */
5364     clearFilter : function(suppressEvent){
5365         if(this.snapshot && this.snapshot != this.data){
5366             this.data = this.snapshot;
5367             delete this.snapshot;
5368             if(suppressEvent !== true){
5369                 this.fireEvent("datachanged", this);
5370             }
5371         }
5372     },
5373
5374     // private
5375     afterEdit : function(record){
5376         if(this.modified.indexOf(record) == -1){
5377             this.modified.push(record);
5378         }
5379         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5380     },
5381     
5382     // private
5383     afterReject : function(record){
5384         this.modified.remove(record);
5385         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5386     },
5387
5388     // private
5389     afterCommit : function(record){
5390         this.modified.remove(record);
5391         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5392     },
5393
5394     /**
5395      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5396      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5397      */
5398     commitChanges : function(){
5399         var m = this.modified.slice(0);
5400         this.modified = [];
5401         for(var i = 0, len = m.length; i < len; i++){
5402             m[i].commit();
5403         }
5404     },
5405
5406     /**
5407      * Cancel outstanding changes on all changed records.
5408      */
5409     rejectChanges : function(){
5410         var m = this.modified.slice(0);
5411         this.modified = [];
5412         for(var i = 0, len = m.length; i < len; i++){
5413             m[i].reject();
5414         }
5415     },
5416
5417     onMetaChange : function(meta, rtype, o){
5418         this.recordType = rtype;
5419         this.fields = rtype.prototype.fields;
5420         delete this.snapshot;
5421         this.sortInfo = meta.sortInfo || this.sortInfo;
5422         this.modified = [];
5423         this.fireEvent('metachange', this, this.reader.meta);
5424     },
5425     
5426     moveIndex : function(data, type)
5427     {
5428         var index = this.indexOf(data);
5429         
5430         var newIndex = index + type;
5431         
5432         this.remove(data);
5433         
5434         this.insert(newIndex, data);
5435         
5436     }
5437 });/*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447
5448 /**
5449  * @class Roo.data.SimpleStore
5450  * @extends Roo.data.Store
5451  * Small helper class to make creating Stores from Array data easier.
5452  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5453  * @cfg {Array} fields An array of field definition objects, or field name strings.
5454  * @cfg {Array} data The multi-dimensional array of data
5455  * @constructor
5456  * @param {Object} config
5457  */
5458 Roo.data.SimpleStore = function(config){
5459     Roo.data.SimpleStore.superclass.constructor.call(this, {
5460         isLocal : true,
5461         reader: new Roo.data.ArrayReader({
5462                 id: config.id
5463             },
5464             Roo.data.Record.create(config.fields)
5465         ),
5466         proxy : new Roo.data.MemoryProxy(config.data)
5467     });
5468     this.load();
5469 };
5470 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5471  * Based on:
5472  * Ext JS Library 1.1.1
5473  * Copyright(c) 2006-2007, Ext JS, LLC.
5474  *
5475  * Originally Released Under LGPL - original licence link has changed is not relivant.
5476  *
5477  * Fork - LGPL
5478  * <script type="text/javascript">
5479  */
5480
5481 /**
5482 /**
5483  * @extends Roo.data.Store
5484  * @class Roo.data.JsonStore
5485  * Small helper class to make creating Stores for JSON data easier. <br/>
5486 <pre><code>
5487 var store = new Roo.data.JsonStore({
5488     url: 'get-images.php',
5489     root: 'images',
5490     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5491 });
5492 </code></pre>
5493  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5494  * JsonReader and HttpProxy (unless inline data is provided).</b>
5495  * @cfg {Array} fields An array of field definition objects, or field name strings.
5496  * @constructor
5497  * @param {Object} config
5498  */
5499 Roo.data.JsonStore = function(c){
5500     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5501         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5502         reader: new Roo.data.JsonReader(c, c.fields)
5503     }));
5504 };
5505 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5506  * Based on:
5507  * Ext JS Library 1.1.1
5508  * Copyright(c) 2006-2007, Ext JS, LLC.
5509  *
5510  * Originally Released Under LGPL - original licence link has changed is not relivant.
5511  *
5512  * Fork - LGPL
5513  * <script type="text/javascript">
5514  */
5515
5516  
5517 Roo.data.Field = function(config){
5518     if(typeof config == "string"){
5519         config = {name: config};
5520     }
5521     Roo.apply(this, config);
5522     
5523     if(!this.type){
5524         this.type = "auto";
5525     }
5526     
5527     var st = Roo.data.SortTypes;
5528     // named sortTypes are supported, here we look them up
5529     if(typeof this.sortType == "string"){
5530         this.sortType = st[this.sortType];
5531     }
5532     
5533     // set default sortType for strings and dates
5534     if(!this.sortType){
5535         switch(this.type){
5536             case "string":
5537                 this.sortType = st.asUCString;
5538                 break;
5539             case "date":
5540                 this.sortType = st.asDate;
5541                 break;
5542             default:
5543                 this.sortType = st.none;
5544         }
5545     }
5546
5547     // define once
5548     var stripRe = /[\$,%]/g;
5549
5550     // prebuilt conversion function for this field, instead of
5551     // switching every time we're reading a value
5552     if(!this.convert){
5553         var cv, dateFormat = this.dateFormat;
5554         switch(this.type){
5555             case "":
5556             case "auto":
5557             case undefined:
5558                 cv = function(v){ return v; };
5559                 break;
5560             case "string":
5561                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5562                 break;
5563             case "int":
5564                 cv = function(v){
5565                     return v !== undefined && v !== null && v !== '' ?
5566                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5567                     };
5568                 break;
5569             case "float":
5570                 cv = function(v){
5571                     return v !== undefined && v !== null && v !== '' ?
5572                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5573                     };
5574                 break;
5575             case "bool":
5576             case "boolean":
5577                 cv = function(v){ return v === true || v === "true" || v == 1; };
5578                 break;
5579             case "date":
5580                 cv = function(v){
5581                     if(!v){
5582                         return '';
5583                     }
5584                     if(v instanceof Date){
5585                         return v;
5586                     }
5587                     if(dateFormat){
5588                         if(dateFormat == "timestamp"){
5589                             return new Date(v*1000);
5590                         }
5591                         return Date.parseDate(v, dateFormat);
5592                     }
5593                     var parsed = Date.parse(v);
5594                     return parsed ? new Date(parsed) : null;
5595                 };
5596              break;
5597             
5598         }
5599         this.convert = cv;
5600     }
5601 };
5602
5603 Roo.data.Field.prototype = {
5604     dateFormat: null,
5605     defaultValue: "",
5606     mapping: null,
5607     sortType : null,
5608     sortDir : "ASC"
5609 };/*
5610  * Based on:
5611  * Ext JS Library 1.1.1
5612  * Copyright(c) 2006-2007, Ext JS, LLC.
5613  *
5614  * Originally Released Under LGPL - original licence link has changed is not relivant.
5615  *
5616  * Fork - LGPL
5617  * <script type="text/javascript">
5618  */
5619  
5620 // Base class for reading structured data from a data source.  This class is intended to be
5621 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5622
5623 /**
5624  * @class Roo.data.DataReader
5625  * Base class for reading structured data from a data source.  This class is intended to be
5626  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5627  */
5628
5629 Roo.data.DataReader = function(meta, recordType){
5630     
5631     this.meta = meta;
5632     
5633     this.recordType = recordType instanceof Array ? 
5634         Roo.data.Record.create(recordType) : recordType;
5635 };
5636
5637 Roo.data.DataReader.prototype = {
5638      /**
5639      * Create an empty record
5640      * @param {Object} data (optional) - overlay some values
5641      * @return {Roo.data.Record} record created.
5642      */
5643     newRow :  function(d) {
5644         var da =  {};
5645         this.recordType.prototype.fields.each(function(c) {
5646             switch( c.type) {
5647                 case 'int' : da[c.name] = 0; break;
5648                 case 'date' : da[c.name] = new Date(); break;
5649                 case 'float' : da[c.name] = 0.0; break;
5650                 case 'boolean' : da[c.name] = false; break;
5651                 default : da[c.name] = ""; break;
5652             }
5653             
5654         });
5655         return new this.recordType(Roo.apply(da, d));
5656     }
5657     
5658 };/*
5659  * Based on:
5660  * Ext JS Library 1.1.1
5661  * Copyright(c) 2006-2007, Ext JS, LLC.
5662  *
5663  * Originally Released Under LGPL - original licence link has changed is not relivant.
5664  *
5665  * Fork - LGPL
5666  * <script type="text/javascript">
5667  */
5668
5669 /**
5670  * @class Roo.data.DataProxy
5671  * @extends Roo.data.Observable
5672  * This class is an abstract base class for implementations which provide retrieval of
5673  * unformatted data objects.<br>
5674  * <p>
5675  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5676  * (of the appropriate type which knows how to parse the data object) to provide a block of
5677  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5678  * <p>
5679  * Custom implementations must implement the load method as described in
5680  * {@link Roo.data.HttpProxy#load}.
5681  */
5682 Roo.data.DataProxy = function(){
5683     this.addEvents({
5684         /**
5685          * @event beforeload
5686          * Fires before a network request is made to retrieve a data object.
5687          * @param {Object} This DataProxy object.
5688          * @param {Object} params The params parameter to the load function.
5689          */
5690         beforeload : true,
5691         /**
5692          * @event load
5693          * Fires before the load method's callback is called.
5694          * @param {Object} This DataProxy object.
5695          * @param {Object} o The data object.
5696          * @param {Object} arg The callback argument object passed to the load function.
5697          */
5698         load : true,
5699         /**
5700          * @event loadexception
5701          * Fires if an Exception occurs during data retrieval.
5702          * @param {Object} This DataProxy object.
5703          * @param {Object} o The data object.
5704          * @param {Object} arg The callback argument object passed to the load function.
5705          * @param {Object} e The Exception.
5706          */
5707         loadexception : true
5708     });
5709     Roo.data.DataProxy.superclass.constructor.call(this);
5710 };
5711
5712 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5713
5714     /**
5715      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5716      */
5717 /*
5718  * Based on:
5719  * Ext JS Library 1.1.1
5720  * Copyright(c) 2006-2007, Ext JS, LLC.
5721  *
5722  * Originally Released Under LGPL - original licence link has changed is not relivant.
5723  *
5724  * Fork - LGPL
5725  * <script type="text/javascript">
5726  */
5727 /**
5728  * @class Roo.data.MemoryProxy
5729  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5730  * to the Reader when its load method is called.
5731  * @constructor
5732  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5733  */
5734 Roo.data.MemoryProxy = function(data){
5735     if (data.data) {
5736         data = data.data;
5737     }
5738     Roo.data.MemoryProxy.superclass.constructor.call(this);
5739     this.data = data;
5740 };
5741
5742 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5743     /**
5744      * Load data from the requested source (in this case an in-memory
5745      * data object passed to the constructor), read the data object into
5746      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5747      * process that block using the passed callback.
5748      * @param {Object} params This parameter is not used by the MemoryProxy class.
5749      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5750      * object into a block of Roo.data.Records.
5751      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5752      * The function must be passed <ul>
5753      * <li>The Record block object</li>
5754      * <li>The "arg" argument from the load function</li>
5755      * <li>A boolean success indicator</li>
5756      * </ul>
5757      * @param {Object} scope The scope in which to call the callback
5758      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5759      */
5760     load : function(params, reader, callback, scope, arg){
5761         params = params || {};
5762         var result;
5763         try {
5764             result = reader.readRecords(this.data);
5765         }catch(e){
5766             this.fireEvent("loadexception", this, arg, null, e);
5767             callback.call(scope, null, arg, false);
5768             return;
5769         }
5770         callback.call(scope, result, arg, true);
5771     },
5772     
5773     // private
5774     update : function(params, records){
5775         
5776     }
5777 });/*
5778  * Based on:
5779  * Ext JS Library 1.1.1
5780  * Copyright(c) 2006-2007, Ext JS, LLC.
5781  *
5782  * Originally Released Under LGPL - original licence link has changed is not relivant.
5783  *
5784  * Fork - LGPL
5785  * <script type="text/javascript">
5786  */
5787 /**
5788  * @class Roo.data.HttpProxy
5789  * @extends Roo.data.DataProxy
5790  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5791  * configured to reference a certain URL.<br><br>
5792  * <p>
5793  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5794  * from which the running page was served.<br><br>
5795  * <p>
5796  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5797  * <p>
5798  * Be aware that to enable the browser to parse an XML document, the server must set
5799  * the Content-Type header in the HTTP response to "text/xml".
5800  * @constructor
5801  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5802  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5803  * will be used to make the request.
5804  */
5805 Roo.data.HttpProxy = function(conn){
5806     Roo.data.HttpProxy.superclass.constructor.call(this);
5807     // is conn a conn config or a real conn?
5808     this.conn = conn;
5809     this.useAjax = !conn || !conn.events;
5810   
5811 };
5812
5813 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5814     // thse are take from connection...
5815     
5816     /**
5817      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5818      */
5819     /**
5820      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5821      * extra parameters to each request made by this object. (defaults to undefined)
5822      */
5823     /**
5824      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5825      *  to each request made by this object. (defaults to undefined)
5826      */
5827     /**
5828      * @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)
5829      */
5830     /**
5831      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5832      */
5833      /**
5834      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5835      * @type Boolean
5836      */
5837   
5838
5839     /**
5840      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5841      * @type Boolean
5842      */
5843     /**
5844      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5845      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5846      * a finer-grained basis than the DataProxy events.
5847      */
5848     getConnection : function(){
5849         return this.useAjax ? Roo.Ajax : this.conn;
5850     },
5851
5852     /**
5853      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5854      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5855      * process that block using the passed callback.
5856      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5857      * for the request to the remote server.
5858      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5859      * object into a block of Roo.data.Records.
5860      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5861      * The function must be passed <ul>
5862      * <li>The Record block object</li>
5863      * <li>The "arg" argument from the load function</li>
5864      * <li>A boolean success indicator</li>
5865      * </ul>
5866      * @param {Object} scope The scope in which to call the callback
5867      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5868      */
5869     load : function(params, reader, callback, scope, arg){
5870         if(this.fireEvent("beforeload", this, params) !== false){
5871             var  o = {
5872                 params : params || {},
5873                 request: {
5874                     callback : callback,
5875                     scope : scope,
5876                     arg : arg
5877                 },
5878                 reader: reader,
5879                 callback : this.loadResponse,
5880                 scope: this
5881             };
5882             if(this.useAjax){
5883                 Roo.applyIf(o, this.conn);
5884                 if(this.activeRequest){
5885                     Roo.Ajax.abort(this.activeRequest);
5886                 }
5887                 this.activeRequest = Roo.Ajax.request(o);
5888             }else{
5889                 this.conn.request(o);
5890             }
5891         }else{
5892             callback.call(scope||this, null, arg, false);
5893         }
5894     },
5895
5896     // private
5897     loadResponse : function(o, success, response){
5898         delete this.activeRequest;
5899         if(!success){
5900             this.fireEvent("loadexception", this, o, response);
5901             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5902             return;
5903         }
5904         var result;
5905         try {
5906             result = o.reader.read(response);
5907         }catch(e){
5908             this.fireEvent("loadexception", this, o, response, e);
5909             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5910             return;
5911         }
5912         
5913         this.fireEvent("load", this, o, o.request.arg);
5914         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5915     },
5916
5917     // private
5918     update : function(dataSet){
5919
5920     },
5921
5922     // private
5923     updateResponse : function(dataSet){
5924
5925     }
5926 });/*
5927  * Based on:
5928  * Ext JS Library 1.1.1
5929  * Copyright(c) 2006-2007, Ext JS, LLC.
5930  *
5931  * Originally Released Under LGPL - original licence link has changed is not relivant.
5932  *
5933  * Fork - LGPL
5934  * <script type="text/javascript">
5935  */
5936
5937 /**
5938  * @class Roo.data.ScriptTagProxy
5939  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5940  * other than the originating domain of the running page.<br><br>
5941  * <p>
5942  * <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
5943  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5944  * <p>
5945  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5946  * source code that is used as the source inside a &lt;script> tag.<br><br>
5947  * <p>
5948  * In order for the browser to process the returned data, the server must wrap the data object
5949  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5950  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5951  * depending on whether the callback name was passed:
5952  * <p>
5953  * <pre><code>
5954 boolean scriptTag = false;
5955 String cb = request.getParameter("callback");
5956 if (cb != null) {
5957     scriptTag = true;
5958     response.setContentType("text/javascript");
5959 } else {
5960     response.setContentType("application/x-json");
5961 }
5962 Writer out = response.getWriter();
5963 if (scriptTag) {
5964     out.write(cb + "(");
5965 }
5966 out.print(dataBlock.toJsonString());
5967 if (scriptTag) {
5968     out.write(");");
5969 }
5970 </pre></code>
5971  *
5972  * @constructor
5973  * @param {Object} config A configuration object.
5974  */
5975 Roo.data.ScriptTagProxy = function(config){
5976     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5977     Roo.apply(this, config);
5978     this.head = document.getElementsByTagName("head")[0];
5979 };
5980
5981 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5982
5983 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5984     /**
5985      * @cfg {String} url The URL from which to request the data object.
5986      */
5987     /**
5988      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5989      */
5990     timeout : 30000,
5991     /**
5992      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5993      * the server the name of the callback function set up by the load call to process the returned data object.
5994      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5995      * javascript output which calls this named function passing the data object as its only parameter.
5996      */
5997     callbackParam : "callback",
5998     /**
5999      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6000      * name to the request.
6001      */
6002     nocache : true,
6003
6004     /**
6005      * Load data from the configured URL, read the data object into
6006      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6007      * process that block using the passed callback.
6008      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6009      * for the request to the remote server.
6010      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6011      * object into a block of Roo.data.Records.
6012      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6013      * The function must be passed <ul>
6014      * <li>The Record block object</li>
6015      * <li>The "arg" argument from the load function</li>
6016      * <li>A boolean success indicator</li>
6017      * </ul>
6018      * @param {Object} scope The scope in which to call the callback
6019      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6020      */
6021     load : function(params, reader, callback, scope, arg){
6022         if(this.fireEvent("beforeload", this, params) !== false){
6023
6024             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6025
6026             var url = this.url;
6027             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6028             if(this.nocache){
6029                 url += "&_dc=" + (new Date().getTime());
6030             }
6031             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6032             var trans = {
6033                 id : transId,
6034                 cb : "stcCallback"+transId,
6035                 scriptId : "stcScript"+transId,
6036                 params : params,
6037                 arg : arg,
6038                 url : url,
6039                 callback : callback,
6040                 scope : scope,
6041                 reader : reader
6042             };
6043             var conn = this;
6044
6045             window[trans.cb] = function(o){
6046                 conn.handleResponse(o, trans);
6047             };
6048
6049             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6050
6051             if(this.autoAbort !== false){
6052                 this.abort();
6053             }
6054
6055             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6056
6057             var script = document.createElement("script");
6058             script.setAttribute("src", url);
6059             script.setAttribute("type", "text/javascript");
6060             script.setAttribute("id", trans.scriptId);
6061             this.head.appendChild(script);
6062
6063             this.trans = trans;
6064         }else{
6065             callback.call(scope||this, null, arg, false);
6066         }
6067     },
6068
6069     // private
6070     isLoading : function(){
6071         return this.trans ? true : false;
6072     },
6073
6074     /**
6075      * Abort the current server request.
6076      */
6077     abort : function(){
6078         if(this.isLoading()){
6079             this.destroyTrans(this.trans);
6080         }
6081     },
6082
6083     // private
6084     destroyTrans : function(trans, isLoaded){
6085         this.head.removeChild(document.getElementById(trans.scriptId));
6086         clearTimeout(trans.timeoutId);
6087         if(isLoaded){
6088             window[trans.cb] = undefined;
6089             try{
6090                 delete window[trans.cb];
6091             }catch(e){}
6092         }else{
6093             // if hasn't been loaded, wait for load to remove it to prevent script error
6094             window[trans.cb] = function(){
6095                 window[trans.cb] = undefined;
6096                 try{
6097                     delete window[trans.cb];
6098                 }catch(e){}
6099             };
6100         }
6101     },
6102
6103     // private
6104     handleResponse : function(o, trans){
6105         this.trans = false;
6106         this.destroyTrans(trans, true);
6107         var result;
6108         try {
6109             result = trans.reader.readRecords(o);
6110         }catch(e){
6111             this.fireEvent("loadexception", this, o, trans.arg, e);
6112             trans.callback.call(trans.scope||window, null, trans.arg, false);
6113             return;
6114         }
6115         this.fireEvent("load", this, o, trans.arg);
6116         trans.callback.call(trans.scope||window, result, trans.arg, true);
6117     },
6118
6119     // private
6120     handleFailure : function(trans){
6121         this.trans = false;
6122         this.destroyTrans(trans, false);
6123         this.fireEvent("loadexception", this, null, trans.arg);
6124         trans.callback.call(trans.scope||window, null, trans.arg, false);
6125     }
6126 });/*
6127  * Based on:
6128  * Ext JS Library 1.1.1
6129  * Copyright(c) 2006-2007, Ext JS, LLC.
6130  *
6131  * Originally Released Under LGPL - original licence link has changed is not relivant.
6132  *
6133  * Fork - LGPL
6134  * <script type="text/javascript">
6135  */
6136
6137 /**
6138  * @class Roo.data.JsonReader
6139  * @extends Roo.data.DataReader
6140  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6141  * based on mappings in a provided Roo.data.Record constructor.
6142  * 
6143  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6144  * in the reply previously. 
6145  * 
6146  * <p>
6147  * Example code:
6148  * <pre><code>
6149 var RecordDef = Roo.data.Record.create([
6150     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6151     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6152 ]);
6153 var myReader = new Roo.data.JsonReader({
6154     totalProperty: "results",    // The property which contains the total dataset size (optional)
6155     root: "rows",                // The property which contains an Array of row objects
6156     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6157 }, RecordDef);
6158 </code></pre>
6159  * <p>
6160  * This would consume a JSON file like this:
6161  * <pre><code>
6162 { 'results': 2, 'rows': [
6163     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6164     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6165 }
6166 </code></pre>
6167  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6168  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6169  * paged from the remote server.
6170  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6171  * @cfg {String} root name of the property which contains the Array of row objects.
6172  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6173  * @cfg {Array} fields Array of field definition objects
6174  * @constructor
6175  * Create a new JsonReader
6176  * @param {Object} meta Metadata configuration options
6177  * @param {Object} recordType Either an Array of field definition objects,
6178  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6179  */
6180 Roo.data.JsonReader = function(meta, recordType){
6181     
6182     meta = meta || {};
6183     // set some defaults:
6184     Roo.applyIf(meta, {
6185         totalProperty: 'total',
6186         successProperty : 'success',
6187         root : 'data',
6188         id : 'id'
6189     });
6190     
6191     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6192 };
6193 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6194     
6195     /**
6196      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6197      * Used by Store query builder to append _requestMeta to params.
6198      * 
6199      */
6200     metaFromRemote : false,
6201     /**
6202      * This method is only used by a DataProxy which has retrieved data from a remote server.
6203      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6204      * @return {Object} data A data block which is used by an Roo.data.Store object as
6205      * a cache of Roo.data.Records.
6206      */
6207     read : function(response){
6208         var json = response.responseText;
6209        
6210         var o = /* eval:var:o */ eval("("+json+")");
6211         if(!o) {
6212             throw {message: "JsonReader.read: Json object not found"};
6213         }
6214         
6215         if(o.metaData){
6216             
6217             delete this.ef;
6218             this.metaFromRemote = true;
6219             this.meta = o.metaData;
6220             this.recordType = Roo.data.Record.create(o.metaData.fields);
6221             this.onMetaChange(this.meta, this.recordType, o);
6222         }
6223         return this.readRecords(o);
6224     },
6225
6226     // private function a store will implement
6227     onMetaChange : function(meta, recordType, o){
6228
6229     },
6230
6231     /**
6232          * @ignore
6233          */
6234     simpleAccess: function(obj, subsc) {
6235         return obj[subsc];
6236     },
6237
6238         /**
6239          * @ignore
6240          */
6241     getJsonAccessor: function(){
6242         var re = /[\[\.]/;
6243         return function(expr) {
6244             try {
6245                 return(re.test(expr))
6246                     ? new Function("obj", "return obj." + expr)
6247                     : function(obj){
6248                         return obj[expr];
6249                     };
6250             } catch(e){}
6251             return Roo.emptyFn;
6252         };
6253     }(),
6254
6255     /**
6256      * Create a data block containing Roo.data.Records from an XML document.
6257      * @param {Object} o An object which contains an Array of row objects in the property specified
6258      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6259      * which contains the total size of the dataset.
6260      * @return {Object} data A data block which is used by an Roo.data.Store object as
6261      * a cache of Roo.data.Records.
6262      */
6263     readRecords : function(o){
6264         /**
6265          * After any data loads, the raw JSON data is available for further custom processing.
6266          * @type Object
6267          */
6268         this.o = o;
6269         var s = this.meta, Record = this.recordType,
6270             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6271
6272 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6273         if (!this.ef) {
6274             if(s.totalProperty) {
6275                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6276                 }
6277                 if(s.successProperty) {
6278                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6279                 }
6280                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6281                 if (s.id) {
6282                         var g = this.getJsonAccessor(s.id);
6283                         this.getId = function(rec) {
6284                                 var r = g(rec);  
6285                                 return (r === undefined || r === "") ? null : r;
6286                         };
6287                 } else {
6288                         this.getId = function(){return null;};
6289                 }
6290             this.ef = [];
6291             for(var jj = 0; jj < fl; jj++){
6292                 f = fi[jj];
6293                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6294                 this.ef[jj] = this.getJsonAccessor(map);
6295             }
6296         }
6297
6298         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6299         if(s.totalProperty){
6300             var vt = parseInt(this.getTotal(o), 10);
6301             if(!isNaN(vt)){
6302                 totalRecords = vt;
6303             }
6304         }
6305         if(s.successProperty){
6306             var vs = this.getSuccess(o);
6307             if(vs === false || vs === 'false'){
6308                 success = false;
6309             }
6310         }
6311         var records = [];
6312         for(var i = 0; i < c; i++){
6313                 var n = root[i];
6314             var values = {};
6315             var id = this.getId(n);
6316             for(var j = 0; j < fl; j++){
6317                 f = fi[j];
6318             var v = this.ef[j](n);
6319             if (!f.convert) {
6320                 Roo.log('missing convert for ' + f.name);
6321                 Roo.log(f);
6322                 continue;
6323             }
6324             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6325             }
6326             var record = new Record(values, id);
6327             record.json = n;
6328             records[i] = record;
6329         }
6330         return {
6331             raw : o,
6332             success : success,
6333             records : records,
6334             totalRecords : totalRecords
6335         };
6336     }
6337 });/*
6338  * Based on:
6339  * Ext JS Library 1.1.1
6340  * Copyright(c) 2006-2007, Ext JS, LLC.
6341  *
6342  * Originally Released Under LGPL - original licence link has changed is not relivant.
6343  *
6344  * Fork - LGPL
6345  * <script type="text/javascript">
6346  */
6347
6348 /**
6349  * @class Roo.data.XmlReader
6350  * @extends Roo.data.DataReader
6351  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6352  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6353  * <p>
6354  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6355  * header in the HTTP response must be set to "text/xml".</em>
6356  * <p>
6357  * Example code:
6358  * <pre><code>
6359 var RecordDef = Roo.data.Record.create([
6360    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6361    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6362 ]);
6363 var myReader = new Roo.data.XmlReader({
6364    totalRecords: "results", // The element which contains the total dataset size (optional)
6365    record: "row",           // The repeated element which contains row information
6366    id: "id"                 // The element within the row that provides an ID for the record (optional)
6367 }, RecordDef);
6368 </code></pre>
6369  * <p>
6370  * This would consume an XML file like this:
6371  * <pre><code>
6372 &lt;?xml?>
6373 &lt;dataset>
6374  &lt;results>2&lt;/results>
6375  &lt;row>
6376    &lt;id>1&lt;/id>
6377    &lt;name>Bill&lt;/name>
6378    &lt;occupation>Gardener&lt;/occupation>
6379  &lt;/row>
6380  &lt;row>
6381    &lt;id>2&lt;/id>
6382    &lt;name>Ben&lt;/name>
6383    &lt;occupation>Horticulturalist&lt;/occupation>
6384  &lt;/row>
6385 &lt;/dataset>
6386 </code></pre>
6387  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6388  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6389  * paged from the remote server.
6390  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6391  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6392  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6393  * a record identifier value.
6394  * @constructor
6395  * Create a new XmlReader
6396  * @param {Object} meta Metadata configuration options
6397  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6398  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6399  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6400  */
6401 Roo.data.XmlReader = function(meta, recordType){
6402     meta = meta || {};
6403     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6404 };
6405 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6406     /**
6407      * This method is only used by a DataProxy which has retrieved data from a remote server.
6408          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6409          * to contain a method called 'responseXML' that returns an XML document object.
6410      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6411      * a cache of Roo.data.Records.
6412      */
6413     read : function(response){
6414         var doc = response.responseXML;
6415         if(!doc) {
6416             throw {message: "XmlReader.read: XML Document not available"};
6417         }
6418         return this.readRecords(doc);
6419     },
6420
6421     /**
6422      * Create a data block containing Roo.data.Records from an XML document.
6423          * @param {Object} doc A parsed XML document.
6424      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6425      * a cache of Roo.data.Records.
6426      */
6427     readRecords : function(doc){
6428         /**
6429          * After any data loads/reads, the raw XML Document is available for further custom processing.
6430          * @type XMLDocument
6431          */
6432         this.xmlData = doc;
6433         var root = doc.documentElement || doc;
6434         var q = Roo.DomQuery;
6435         var recordType = this.recordType, fields = recordType.prototype.fields;
6436         var sid = this.meta.id;
6437         var totalRecords = 0, success = true;
6438         if(this.meta.totalRecords){
6439             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6440         }
6441         
6442         if(this.meta.success){
6443             var sv = q.selectValue(this.meta.success, root, true);
6444             success = sv !== false && sv !== 'false';
6445         }
6446         var records = [];
6447         var ns = q.select(this.meta.record, root);
6448         for(var i = 0, len = ns.length; i < len; i++) {
6449                 var n = ns[i];
6450                 var values = {};
6451                 var id = sid ? q.selectValue(sid, n) : undefined;
6452                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6453                     var f = fields.items[j];
6454                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6455                     v = f.convert(v);
6456                     values[f.name] = v;
6457                 }
6458                 var record = new recordType(values, id);
6459                 record.node = n;
6460                 records[records.length] = record;
6461             }
6462
6463             return {
6464                 success : success,
6465                 records : records,
6466                 totalRecords : totalRecords || records.length
6467             };
6468     }
6469 });/*
6470  * Based on:
6471  * Ext JS Library 1.1.1
6472  * Copyright(c) 2006-2007, Ext JS, LLC.
6473  *
6474  * Originally Released Under LGPL - original licence link has changed is not relivant.
6475  *
6476  * Fork - LGPL
6477  * <script type="text/javascript">
6478  */
6479
6480 /**
6481  * @class Roo.data.ArrayReader
6482  * @extends Roo.data.DataReader
6483  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6484  * Each element of that Array represents a row of data fields. The
6485  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6486  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6487  * <p>
6488  * Example code:.
6489  * <pre><code>
6490 var RecordDef = Roo.data.Record.create([
6491     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6492     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6493 ]);
6494 var myReader = new Roo.data.ArrayReader({
6495     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6496 }, RecordDef);
6497 </code></pre>
6498  * <p>
6499  * This would consume an Array like this:
6500  * <pre><code>
6501 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6502   </code></pre>
6503  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6504  * @constructor
6505  * Create a new JsonReader
6506  * @param {Object} meta Metadata configuration options.
6507  * @param {Object} recordType Either an Array of field definition objects
6508  * as specified to {@link Roo.data.Record#create},
6509  * or an {@link Roo.data.Record} object
6510  * created using {@link Roo.data.Record#create}.
6511  */
6512 Roo.data.ArrayReader = function(meta, recordType){
6513     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6514 };
6515
6516 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6517     /**
6518      * Create a data block containing Roo.data.Records from an XML document.
6519      * @param {Object} o An Array of row objects which represents the dataset.
6520      * @return {Object} data A data block which is used by an Roo.data.Store object as
6521      * a cache of Roo.data.Records.
6522      */
6523     readRecords : function(o){
6524         var sid = this.meta ? this.meta.id : null;
6525         var recordType = this.recordType, fields = recordType.prototype.fields;
6526         var records = [];
6527         var root = o;
6528             for(var i = 0; i < root.length; i++){
6529                     var n = root[i];
6530                 var values = {};
6531                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6532                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6533                 var f = fields.items[j];
6534                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6535                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6536                 v = f.convert(v);
6537                 values[f.name] = v;
6538             }
6539                 var record = new recordType(values, id);
6540                 record.json = n;
6541                 records[records.length] = record;
6542             }
6543             return {
6544                 records : records,
6545                 totalRecords : records.length
6546             };
6547     }
6548 });/*
6549  * Based on:
6550  * Ext JS Library 1.1.1
6551  * Copyright(c) 2006-2007, Ext JS, LLC.
6552  *
6553  * Originally Released Under LGPL - original licence link has changed is not relivant.
6554  *
6555  * Fork - LGPL
6556  * <script type="text/javascript">
6557  */
6558
6559
6560 /**
6561  * @class Roo.data.Tree
6562  * @extends Roo.util.Observable
6563  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6564  * in the tree have most standard DOM functionality.
6565  * @constructor
6566  * @param {Node} root (optional) The root node
6567  */
6568 Roo.data.Tree = function(root){
6569    this.nodeHash = {};
6570    /**
6571     * The root node for this tree
6572     * @type Node
6573     */
6574    this.root = null;
6575    if(root){
6576        this.setRootNode(root);
6577    }
6578    this.addEvents({
6579        /**
6580         * @event append
6581         * Fires when a new child node is appended to a node in this tree.
6582         * @param {Tree} tree The owner tree
6583         * @param {Node} parent The parent node
6584         * @param {Node} node The newly appended node
6585         * @param {Number} index The index of the newly appended node
6586         */
6587        "append" : true,
6588        /**
6589         * @event remove
6590         * Fires when a child node is removed from a node in this tree.
6591         * @param {Tree} tree The owner tree
6592         * @param {Node} parent The parent node
6593         * @param {Node} node The child node removed
6594         */
6595        "remove" : true,
6596        /**
6597         * @event move
6598         * Fires when a node is moved to a new location in the tree
6599         * @param {Tree} tree The owner tree
6600         * @param {Node} node The node moved
6601         * @param {Node} oldParent The old parent of this node
6602         * @param {Node} newParent The new parent of this node
6603         * @param {Number} index The index it was moved to
6604         */
6605        "move" : true,
6606        /**
6607         * @event insert
6608         * Fires when a new child node is inserted in a node in this tree.
6609         * @param {Tree} tree The owner tree
6610         * @param {Node} parent The parent node
6611         * @param {Node} node The child node inserted
6612         * @param {Node} refNode The child node the node was inserted before
6613         */
6614        "insert" : true,
6615        /**
6616         * @event beforeappend
6617         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6618         * @param {Tree} tree The owner tree
6619         * @param {Node} parent The parent node
6620         * @param {Node} node The child node to be appended
6621         */
6622        "beforeappend" : true,
6623        /**
6624         * @event beforeremove
6625         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6626         * @param {Tree} tree The owner tree
6627         * @param {Node} parent The parent node
6628         * @param {Node} node The child node to be removed
6629         */
6630        "beforeremove" : true,
6631        /**
6632         * @event beforemove
6633         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6634         * @param {Tree} tree The owner tree
6635         * @param {Node} node The node being moved
6636         * @param {Node} oldParent The parent of the node
6637         * @param {Node} newParent The new parent the node is moving to
6638         * @param {Number} index The index it is being moved to
6639         */
6640        "beforemove" : true,
6641        /**
6642         * @event beforeinsert
6643         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6644         * @param {Tree} tree The owner tree
6645         * @param {Node} parent The parent node
6646         * @param {Node} node The child node to be inserted
6647         * @param {Node} refNode The child node the node is being inserted before
6648         */
6649        "beforeinsert" : true
6650    });
6651
6652     Roo.data.Tree.superclass.constructor.call(this);
6653 };
6654
6655 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6656     pathSeparator: "/",
6657
6658     proxyNodeEvent : function(){
6659         return this.fireEvent.apply(this, arguments);
6660     },
6661
6662     /**
6663      * Returns the root node for this tree.
6664      * @return {Node}
6665      */
6666     getRootNode : function(){
6667         return this.root;
6668     },
6669
6670     /**
6671      * Sets the root node for this tree.
6672      * @param {Node} node
6673      * @return {Node}
6674      */
6675     setRootNode : function(node){
6676         this.root = node;
6677         node.ownerTree = this;
6678         node.isRoot = true;
6679         this.registerNode(node);
6680         return node;
6681     },
6682
6683     /**
6684      * Gets a node in this tree by its id.
6685      * @param {String} id
6686      * @return {Node}
6687      */
6688     getNodeById : function(id){
6689         return this.nodeHash[id];
6690     },
6691
6692     registerNode : function(node){
6693         this.nodeHash[node.id] = node;
6694     },
6695
6696     unregisterNode : function(node){
6697         delete this.nodeHash[node.id];
6698     },
6699
6700     toString : function(){
6701         return "[Tree"+(this.id?" "+this.id:"")+"]";
6702     }
6703 });
6704
6705 /**
6706  * @class Roo.data.Node
6707  * @extends Roo.util.Observable
6708  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6709  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6710  * @constructor
6711  * @param {Object} attributes The attributes/config for the node
6712  */
6713 Roo.data.Node = function(attributes){
6714     /**
6715      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6716      * @type {Object}
6717      */
6718     this.attributes = attributes || {};
6719     this.leaf = this.attributes.leaf;
6720     /**
6721      * The node id. @type String
6722      */
6723     this.id = this.attributes.id;
6724     if(!this.id){
6725         this.id = Roo.id(null, "ynode-");
6726         this.attributes.id = this.id;
6727     }
6728      
6729     
6730     /**
6731      * All child nodes of this node. @type Array
6732      */
6733     this.childNodes = [];
6734     if(!this.childNodes.indexOf){ // indexOf is a must
6735         this.childNodes.indexOf = function(o){
6736             for(var i = 0, len = this.length; i < len; i++){
6737                 if(this[i] == o) {
6738                     return i;
6739                 }
6740             }
6741             return -1;
6742         };
6743     }
6744     /**
6745      * The parent node for this node. @type Node
6746      */
6747     this.parentNode = null;
6748     /**
6749      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6750      */
6751     this.firstChild = null;
6752     /**
6753      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6754      */
6755     this.lastChild = null;
6756     /**
6757      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6758      */
6759     this.previousSibling = null;
6760     /**
6761      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6762      */
6763     this.nextSibling = null;
6764
6765     this.addEvents({
6766        /**
6767         * @event append
6768         * Fires when a new child node is appended
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The newly appended node
6772         * @param {Number} index The index of the newly appended node
6773         */
6774        "append" : true,
6775        /**
6776         * @event remove
6777         * Fires when a child node is removed
6778         * @param {Tree} tree The owner tree
6779         * @param {Node} this This node
6780         * @param {Node} node The removed node
6781         */
6782        "remove" : true,
6783        /**
6784         * @event move
6785         * Fires when this node is moved to a new location in the tree
6786         * @param {Tree} tree The owner tree
6787         * @param {Node} this This node
6788         * @param {Node} oldParent The old parent of this node
6789         * @param {Node} newParent The new parent of this node
6790         * @param {Number} index The index it was moved to
6791         */
6792        "move" : true,
6793        /**
6794         * @event insert
6795         * Fires when a new child node is inserted.
6796         * @param {Tree} tree The owner tree
6797         * @param {Node} this This node
6798         * @param {Node} node The child node inserted
6799         * @param {Node} refNode The child node the node was inserted before
6800         */
6801        "insert" : true,
6802        /**
6803         * @event beforeappend
6804         * Fires before a new child is appended, return false to cancel the append.
6805         * @param {Tree} tree The owner tree
6806         * @param {Node} this This node
6807         * @param {Node} node The child node to be appended
6808         */
6809        "beforeappend" : true,
6810        /**
6811         * @event beforeremove
6812         * Fires before a child is removed, return false to cancel the remove.
6813         * @param {Tree} tree The owner tree
6814         * @param {Node} this This node
6815         * @param {Node} node The child node to be removed
6816         */
6817        "beforeremove" : true,
6818        /**
6819         * @event beforemove
6820         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6821         * @param {Tree} tree The owner tree
6822         * @param {Node} this This node
6823         * @param {Node} oldParent The parent of this node
6824         * @param {Node} newParent The new parent this node is moving to
6825         * @param {Number} index The index it is being moved to
6826         */
6827        "beforemove" : true,
6828        /**
6829         * @event beforeinsert
6830         * Fires before a new child is inserted, return false to cancel the insert.
6831         * @param {Tree} tree The owner tree
6832         * @param {Node} this This node
6833         * @param {Node} node The child node to be inserted
6834         * @param {Node} refNode The child node the node is being inserted before
6835         */
6836        "beforeinsert" : true
6837    });
6838     this.listeners = this.attributes.listeners;
6839     Roo.data.Node.superclass.constructor.call(this);
6840 };
6841
6842 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6843     fireEvent : function(evtName){
6844         // first do standard event for this node
6845         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6846             return false;
6847         }
6848         // then bubble it up to the tree if the event wasn't cancelled
6849         var ot = this.getOwnerTree();
6850         if(ot){
6851             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6852                 return false;
6853             }
6854         }
6855         return true;
6856     },
6857
6858     /**
6859      * Returns true if this node is a leaf
6860      * @return {Boolean}
6861      */
6862     isLeaf : function(){
6863         return this.leaf === true;
6864     },
6865
6866     // private
6867     setFirstChild : function(node){
6868         this.firstChild = node;
6869     },
6870
6871     //private
6872     setLastChild : function(node){
6873         this.lastChild = node;
6874     },
6875
6876
6877     /**
6878      * Returns true if this node is the last child of its parent
6879      * @return {Boolean}
6880      */
6881     isLast : function(){
6882        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6883     },
6884
6885     /**
6886      * Returns true if this node is the first child of its parent
6887      * @return {Boolean}
6888      */
6889     isFirst : function(){
6890        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6891     },
6892
6893     hasChildNodes : function(){
6894         return !this.isLeaf() && this.childNodes.length > 0;
6895     },
6896
6897     /**
6898      * Insert node(s) as the last child node of this node.
6899      * @param {Node/Array} node The node or Array of nodes to append
6900      * @return {Node} The appended node if single append, or null if an array was passed
6901      */
6902     appendChild : function(node){
6903         var multi = false;
6904         if(node instanceof Array){
6905             multi = node;
6906         }else if(arguments.length > 1){
6907             multi = arguments;
6908         }
6909         // if passed an array or multiple args do them one by one
6910         if(multi){
6911             for(var i = 0, len = multi.length; i < len; i++) {
6912                 this.appendChild(multi[i]);
6913             }
6914         }else{
6915             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6916                 return false;
6917             }
6918             var index = this.childNodes.length;
6919             var oldParent = node.parentNode;
6920             // it's a move, make sure we move it cleanly
6921             if(oldParent){
6922                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6923                     return false;
6924                 }
6925                 oldParent.removeChild(node);
6926             }
6927             index = this.childNodes.length;
6928             if(index == 0){
6929                 this.setFirstChild(node);
6930             }
6931             this.childNodes.push(node);
6932             node.parentNode = this;
6933             var ps = this.childNodes[index-1];
6934             if(ps){
6935                 node.previousSibling = ps;
6936                 ps.nextSibling = node;
6937             }else{
6938                 node.previousSibling = null;
6939             }
6940             node.nextSibling = null;
6941             this.setLastChild(node);
6942             node.setOwnerTree(this.getOwnerTree());
6943             this.fireEvent("append", this.ownerTree, this, node, index);
6944             if(oldParent){
6945                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6946             }
6947             return node;
6948         }
6949     },
6950
6951     /**
6952      * Removes a child node from this node.
6953      * @param {Node} node The node to remove
6954      * @return {Node} The removed node
6955      */
6956     removeChild : function(node){
6957         var index = this.childNodes.indexOf(node);
6958         if(index == -1){
6959             return false;
6960         }
6961         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6962             return false;
6963         }
6964
6965         // remove it from childNodes collection
6966         this.childNodes.splice(index, 1);
6967
6968         // update siblings
6969         if(node.previousSibling){
6970             node.previousSibling.nextSibling = node.nextSibling;
6971         }
6972         if(node.nextSibling){
6973             node.nextSibling.previousSibling = node.previousSibling;
6974         }
6975
6976         // update child refs
6977         if(this.firstChild == node){
6978             this.setFirstChild(node.nextSibling);
6979         }
6980         if(this.lastChild == node){
6981             this.setLastChild(node.previousSibling);
6982         }
6983
6984         node.setOwnerTree(null);
6985         // clear any references from the node
6986         node.parentNode = null;
6987         node.previousSibling = null;
6988         node.nextSibling = null;
6989         this.fireEvent("remove", this.ownerTree, this, node);
6990         return node;
6991     },
6992
6993     /**
6994      * Inserts the first node before the second node in this nodes childNodes collection.
6995      * @param {Node} node The node to insert
6996      * @param {Node} refNode The node to insert before (if null the node is appended)
6997      * @return {Node} The inserted node
6998      */
6999     insertBefore : function(node, refNode){
7000         if(!refNode){ // like standard Dom, refNode can be null for append
7001             return this.appendChild(node);
7002         }
7003         // nothing to do
7004         if(node == refNode){
7005             return false;
7006         }
7007
7008         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7009             return false;
7010         }
7011         var index = this.childNodes.indexOf(refNode);
7012         var oldParent = node.parentNode;
7013         var refIndex = index;
7014
7015         // when moving internally, indexes will change after remove
7016         if(oldParent == this && this.childNodes.indexOf(node) < index){
7017             refIndex--;
7018         }
7019
7020         // it's a move, make sure we move it cleanly
7021         if(oldParent){
7022             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7023                 return false;
7024             }
7025             oldParent.removeChild(node);
7026         }
7027         if(refIndex == 0){
7028             this.setFirstChild(node);
7029         }
7030         this.childNodes.splice(refIndex, 0, node);
7031         node.parentNode = this;
7032         var ps = this.childNodes[refIndex-1];
7033         if(ps){
7034             node.previousSibling = ps;
7035             ps.nextSibling = node;
7036         }else{
7037             node.previousSibling = null;
7038         }
7039         node.nextSibling = refNode;
7040         refNode.previousSibling = node;
7041         node.setOwnerTree(this.getOwnerTree());
7042         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7043         if(oldParent){
7044             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7045         }
7046         return node;
7047     },
7048
7049     /**
7050      * Returns the child node at the specified index.
7051      * @param {Number} index
7052      * @return {Node}
7053      */
7054     item : function(index){
7055         return this.childNodes[index];
7056     },
7057
7058     /**
7059      * Replaces one child node in this node with another.
7060      * @param {Node} newChild The replacement node
7061      * @param {Node} oldChild The node to replace
7062      * @return {Node} The replaced node
7063      */
7064     replaceChild : function(newChild, oldChild){
7065         this.insertBefore(newChild, oldChild);
7066         this.removeChild(oldChild);
7067         return oldChild;
7068     },
7069
7070     /**
7071      * Returns the index of a child node
7072      * @param {Node} node
7073      * @return {Number} The index of the node or -1 if it was not found
7074      */
7075     indexOf : function(child){
7076         return this.childNodes.indexOf(child);
7077     },
7078
7079     /**
7080      * Returns the tree this node is in.
7081      * @return {Tree}
7082      */
7083     getOwnerTree : function(){
7084         // if it doesn't have one, look for one
7085         if(!this.ownerTree){
7086             var p = this;
7087             while(p){
7088                 if(p.ownerTree){
7089                     this.ownerTree = p.ownerTree;
7090                     break;
7091                 }
7092                 p = p.parentNode;
7093             }
7094         }
7095         return this.ownerTree;
7096     },
7097
7098     /**
7099      * Returns depth of this node (the root node has a depth of 0)
7100      * @return {Number}
7101      */
7102     getDepth : function(){
7103         var depth = 0;
7104         var p = this;
7105         while(p.parentNode){
7106             ++depth;
7107             p = p.parentNode;
7108         }
7109         return depth;
7110     },
7111
7112     // private
7113     setOwnerTree : function(tree){
7114         // if it's move, we need to update everyone
7115         if(tree != this.ownerTree){
7116             if(this.ownerTree){
7117                 this.ownerTree.unregisterNode(this);
7118             }
7119             this.ownerTree = tree;
7120             var cs = this.childNodes;
7121             for(var i = 0, len = cs.length; i < len; i++) {
7122                 cs[i].setOwnerTree(tree);
7123             }
7124             if(tree){
7125                 tree.registerNode(this);
7126             }
7127         }
7128     },
7129
7130     /**
7131      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7132      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7133      * @return {String} The path
7134      */
7135     getPath : function(attr){
7136         attr = attr || "id";
7137         var p = this.parentNode;
7138         var b = [this.attributes[attr]];
7139         while(p){
7140             b.unshift(p.attributes[attr]);
7141             p = p.parentNode;
7142         }
7143         var sep = this.getOwnerTree().pathSeparator;
7144         return sep + b.join(sep);
7145     },
7146
7147     /**
7148      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7149      * function call will be the scope provided or the current node. The arguments to the function
7150      * will be the args provided or the current node. If the function returns false at any point,
7151      * the bubble is stopped.
7152      * @param {Function} fn The function to call
7153      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7154      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7155      */
7156     bubble : function(fn, scope, args){
7157         var p = this;
7158         while(p){
7159             if(fn.call(scope || p, args || p) === false){
7160                 break;
7161             }
7162             p = p.parentNode;
7163         }
7164     },
7165
7166     /**
7167      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7168      * function call will be the scope provided or the current node. The arguments to the function
7169      * will be the args provided or the current node. If the function returns false at any point,
7170      * the cascade is stopped on that branch.
7171      * @param {Function} fn The function to call
7172      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7173      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7174      */
7175     cascade : function(fn, scope, args){
7176         if(fn.call(scope || this, args || this) !== false){
7177             var cs = this.childNodes;
7178             for(var i = 0, len = cs.length; i < len; i++) {
7179                 cs[i].cascade(fn, scope, args);
7180             }
7181         }
7182     },
7183
7184     /**
7185      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7186      * function call will be the scope provided or the current node. The arguments to the function
7187      * will be the args provided or the current node. If the function returns false at any point,
7188      * the iteration stops.
7189      * @param {Function} fn The function to call
7190      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7191      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7192      */
7193     eachChild : function(fn, scope, args){
7194         var cs = this.childNodes;
7195         for(var i = 0, len = cs.length; i < len; i++) {
7196                 if(fn.call(scope || this, args || cs[i]) === false){
7197                     break;
7198                 }
7199         }
7200     },
7201
7202     /**
7203      * Finds the first child that has the attribute with the specified value.
7204      * @param {String} attribute The attribute name
7205      * @param {Mixed} value The value to search for
7206      * @return {Node} The found child or null if none was found
7207      */
7208     findChild : function(attribute, value){
7209         var cs = this.childNodes;
7210         for(var i = 0, len = cs.length; i < len; i++) {
7211                 if(cs[i].attributes[attribute] == value){
7212                     return cs[i];
7213                 }
7214         }
7215         return null;
7216     },
7217
7218     /**
7219      * Finds the first child by a custom function. The child matches if the function passed
7220      * returns true.
7221      * @param {Function} fn
7222      * @param {Object} scope (optional)
7223      * @return {Node} The found child or null if none was found
7224      */
7225     findChildBy : function(fn, scope){
7226         var cs = this.childNodes;
7227         for(var i = 0, len = cs.length; i < len; i++) {
7228                 if(fn.call(scope||cs[i], cs[i]) === true){
7229                     return cs[i];
7230                 }
7231         }
7232         return null;
7233     },
7234
7235     /**
7236      * Sorts this nodes children using the supplied sort function
7237      * @param {Function} fn
7238      * @param {Object} scope (optional)
7239      */
7240     sort : function(fn, scope){
7241         var cs = this.childNodes;
7242         var len = cs.length;
7243         if(len > 0){
7244             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7245             cs.sort(sortFn);
7246             for(var i = 0; i < len; i++){
7247                 var n = cs[i];
7248                 n.previousSibling = cs[i-1];
7249                 n.nextSibling = cs[i+1];
7250                 if(i == 0){
7251                     this.setFirstChild(n);
7252                 }
7253                 if(i == len-1){
7254                     this.setLastChild(n);
7255                 }
7256             }
7257         }
7258     },
7259
7260     /**
7261      * Returns true if this node is an ancestor (at any point) of the passed node.
7262      * @param {Node} node
7263      * @return {Boolean}
7264      */
7265     contains : function(node){
7266         return node.isAncestor(this);
7267     },
7268
7269     /**
7270      * Returns true if the passed node is an ancestor (at any point) of this node.
7271      * @param {Node} node
7272      * @return {Boolean}
7273      */
7274     isAncestor : function(node){
7275         var p = this.parentNode;
7276         while(p){
7277             if(p == node){
7278                 return true;
7279             }
7280             p = p.parentNode;
7281         }
7282         return false;
7283     },
7284
7285     toString : function(){
7286         return "[Node"+(this.id?" "+this.id:"")+"]";
7287     }
7288 });/*
7289  * Based on:
7290  * Ext JS Library 1.1.1
7291  * Copyright(c) 2006-2007, Ext JS, LLC.
7292  *
7293  * Originally Released Under LGPL - original licence link has changed is not relivant.
7294  *
7295  * Fork - LGPL
7296  * <script type="text/javascript">
7297  */
7298  (function(){ 
7299 /**
7300  * @class Roo.Layer
7301  * @extends Roo.Element
7302  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7303  * automatic maintaining of shadow/shim positions.
7304  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7305  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7306  * you can pass a string with a CSS class name. False turns off the shadow.
7307  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7308  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7309  * @cfg {String} cls CSS class to add to the element
7310  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7311  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7312  * @constructor
7313  * @param {Object} config An object with config options.
7314  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7315  */
7316
7317 Roo.Layer = function(config, existingEl){
7318     config = config || {};
7319     var dh = Roo.DomHelper;
7320     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7321     if(existingEl){
7322         this.dom = Roo.getDom(existingEl);
7323     }
7324     if(!this.dom){
7325         var o = config.dh || {tag: "div", cls: "x-layer"};
7326         this.dom = dh.append(pel, o);
7327     }
7328     if(config.cls){
7329         this.addClass(config.cls);
7330     }
7331     this.constrain = config.constrain !== false;
7332     this.visibilityMode = Roo.Element.VISIBILITY;
7333     if(config.id){
7334         this.id = this.dom.id = config.id;
7335     }else{
7336         this.id = Roo.id(this.dom);
7337     }
7338     this.zindex = config.zindex || this.getZIndex();
7339     this.position("absolute", this.zindex);
7340     if(config.shadow){
7341         this.shadowOffset = config.shadowOffset || 4;
7342         this.shadow = new Roo.Shadow({
7343             offset : this.shadowOffset,
7344             mode : config.shadow
7345         });
7346     }else{
7347         this.shadowOffset = 0;
7348     }
7349     this.useShim = config.shim !== false && Roo.useShims;
7350     this.useDisplay = config.useDisplay;
7351     this.hide();
7352 };
7353
7354 var supr = Roo.Element.prototype;
7355
7356 // shims are shared among layer to keep from having 100 iframes
7357 var shims = [];
7358
7359 Roo.extend(Roo.Layer, Roo.Element, {
7360
7361     getZIndex : function(){
7362         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7363     },
7364
7365     getShim : function(){
7366         if(!this.useShim){
7367             return null;
7368         }
7369         if(this.shim){
7370             return this.shim;
7371         }
7372         var shim = shims.shift();
7373         if(!shim){
7374             shim = this.createShim();
7375             shim.enableDisplayMode('block');
7376             shim.dom.style.display = 'none';
7377             shim.dom.style.visibility = 'visible';
7378         }
7379         var pn = this.dom.parentNode;
7380         if(shim.dom.parentNode != pn){
7381             pn.insertBefore(shim.dom, this.dom);
7382         }
7383         shim.setStyle('z-index', this.getZIndex()-2);
7384         this.shim = shim;
7385         return shim;
7386     },
7387
7388     hideShim : function(){
7389         if(this.shim){
7390             this.shim.setDisplayed(false);
7391             shims.push(this.shim);
7392             delete this.shim;
7393         }
7394     },
7395
7396     disableShadow : function(){
7397         if(this.shadow){
7398             this.shadowDisabled = true;
7399             this.shadow.hide();
7400             this.lastShadowOffset = this.shadowOffset;
7401             this.shadowOffset = 0;
7402         }
7403     },
7404
7405     enableShadow : function(show){
7406         if(this.shadow){
7407             this.shadowDisabled = false;
7408             this.shadowOffset = this.lastShadowOffset;
7409             delete this.lastShadowOffset;
7410             if(show){
7411                 this.sync(true);
7412             }
7413         }
7414     },
7415
7416     // private
7417     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7418     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7419     sync : function(doShow){
7420         var sw = this.shadow;
7421         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7422             var sh = this.getShim();
7423
7424             var w = this.getWidth(),
7425                 h = this.getHeight();
7426
7427             var l = this.getLeft(true),
7428                 t = this.getTop(true);
7429
7430             if(sw && !this.shadowDisabled){
7431                 if(doShow && !sw.isVisible()){
7432                     sw.show(this);
7433                 }else{
7434                     sw.realign(l, t, w, h);
7435                 }
7436                 if(sh){
7437                     if(doShow){
7438                        sh.show();
7439                     }
7440                     // fit the shim behind the shadow, so it is shimmed too
7441                     var a = sw.adjusts, s = sh.dom.style;
7442                     s.left = (Math.min(l, l+a.l))+"px";
7443                     s.top = (Math.min(t, t+a.t))+"px";
7444                     s.width = (w+a.w)+"px";
7445                     s.height = (h+a.h)+"px";
7446                 }
7447             }else if(sh){
7448                 if(doShow){
7449                    sh.show();
7450                 }
7451                 sh.setSize(w, h);
7452                 sh.setLeftTop(l, t);
7453             }
7454             
7455         }
7456     },
7457
7458     // private
7459     destroy : function(){
7460         this.hideShim();
7461         if(this.shadow){
7462             this.shadow.hide();
7463         }
7464         this.removeAllListeners();
7465         var pn = this.dom.parentNode;
7466         if(pn){
7467             pn.removeChild(this.dom);
7468         }
7469         Roo.Element.uncache(this.id);
7470     },
7471
7472     remove : function(){
7473         this.destroy();
7474     },
7475
7476     // private
7477     beginUpdate : function(){
7478         this.updating = true;
7479     },
7480
7481     // private
7482     endUpdate : function(){
7483         this.updating = false;
7484         this.sync(true);
7485     },
7486
7487     // private
7488     hideUnders : function(negOffset){
7489         if(this.shadow){
7490             this.shadow.hide();
7491         }
7492         this.hideShim();
7493     },
7494
7495     // private
7496     constrainXY : function(){
7497         if(this.constrain){
7498             var vw = Roo.lib.Dom.getViewWidth(),
7499                 vh = Roo.lib.Dom.getViewHeight();
7500             var s = Roo.get(document).getScroll();
7501
7502             var xy = this.getXY();
7503             var x = xy[0], y = xy[1];   
7504             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7505             // only move it if it needs it
7506             var moved = false;
7507             // first validate right/bottom
7508             if((x + w) > vw+s.left){
7509                 x = vw - w - this.shadowOffset;
7510                 moved = true;
7511             }
7512             if((y + h) > vh+s.top){
7513                 y = vh - h - this.shadowOffset;
7514                 moved = true;
7515             }
7516             // then make sure top/left isn't negative
7517             if(x < s.left){
7518                 x = s.left;
7519                 moved = true;
7520             }
7521             if(y < s.top){
7522                 y = s.top;
7523                 moved = true;
7524             }
7525             if(moved){
7526                 if(this.avoidY){
7527                     var ay = this.avoidY;
7528                     if(y <= ay && (y+h) >= ay){
7529                         y = ay-h-5;   
7530                     }
7531                 }
7532                 xy = [x, y];
7533                 this.storeXY(xy);
7534                 supr.setXY.call(this, xy);
7535                 this.sync();
7536             }
7537         }
7538     },
7539
7540     isVisible : function(){
7541         return this.visible;    
7542     },
7543
7544     // private
7545     showAction : function(){
7546         this.visible = true; // track visibility to prevent getStyle calls
7547         if(this.useDisplay === true){
7548             this.setDisplayed("");
7549         }else if(this.lastXY){
7550             supr.setXY.call(this, this.lastXY);
7551         }else if(this.lastLT){
7552             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7553         }
7554     },
7555
7556     // private
7557     hideAction : function(){
7558         this.visible = false;
7559         if(this.useDisplay === true){
7560             this.setDisplayed(false);
7561         }else{
7562             this.setLeftTop(-10000,-10000);
7563         }
7564     },
7565
7566     // overridden Element method
7567     setVisible : function(v, a, d, c, e){
7568         if(v){
7569             this.showAction();
7570         }
7571         if(a && v){
7572             var cb = function(){
7573                 this.sync(true);
7574                 if(c){
7575                     c();
7576                 }
7577             }.createDelegate(this);
7578             supr.setVisible.call(this, true, true, d, cb, e);
7579         }else{
7580             if(!v){
7581                 this.hideUnders(true);
7582             }
7583             var cb = c;
7584             if(a){
7585                 cb = function(){
7586                     this.hideAction();
7587                     if(c){
7588                         c();
7589                     }
7590                 }.createDelegate(this);
7591             }
7592             supr.setVisible.call(this, v, a, d, cb, e);
7593             if(v){
7594                 this.sync(true);
7595             }else if(!a){
7596                 this.hideAction();
7597             }
7598         }
7599     },
7600
7601     storeXY : function(xy){
7602         delete this.lastLT;
7603         this.lastXY = xy;
7604     },
7605
7606     storeLeftTop : function(left, top){
7607         delete this.lastXY;
7608         this.lastLT = [left, top];
7609     },
7610
7611     // private
7612     beforeFx : function(){
7613         this.beforeAction();
7614         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7615     },
7616
7617     // private
7618     afterFx : function(){
7619         Roo.Layer.superclass.afterFx.apply(this, arguments);
7620         this.sync(this.isVisible());
7621     },
7622
7623     // private
7624     beforeAction : function(){
7625         if(!this.updating && this.shadow){
7626             this.shadow.hide();
7627         }
7628     },
7629
7630     // overridden Element method
7631     setLeft : function(left){
7632         this.storeLeftTop(left, this.getTop(true));
7633         supr.setLeft.apply(this, arguments);
7634         this.sync();
7635     },
7636
7637     setTop : function(top){
7638         this.storeLeftTop(this.getLeft(true), top);
7639         supr.setTop.apply(this, arguments);
7640         this.sync();
7641     },
7642
7643     setLeftTop : function(left, top){
7644         this.storeLeftTop(left, top);
7645         supr.setLeftTop.apply(this, arguments);
7646         this.sync();
7647     },
7648
7649     setXY : function(xy, a, d, c, e){
7650         this.fixDisplay();
7651         this.beforeAction();
7652         this.storeXY(xy);
7653         var cb = this.createCB(c);
7654         supr.setXY.call(this, xy, a, d, cb, e);
7655         if(!a){
7656             cb();
7657         }
7658     },
7659
7660     // private
7661     createCB : function(c){
7662         var el = this;
7663         return function(){
7664             el.constrainXY();
7665             el.sync(true);
7666             if(c){
7667                 c();
7668             }
7669         };
7670     },
7671
7672     // overridden Element method
7673     setX : function(x, a, d, c, e){
7674         this.setXY([x, this.getY()], a, d, c, e);
7675     },
7676
7677     // overridden Element method
7678     setY : function(y, a, d, c, e){
7679         this.setXY([this.getX(), y], a, d, c, e);
7680     },
7681
7682     // overridden Element method
7683     setSize : function(w, h, a, d, c, e){
7684         this.beforeAction();
7685         var cb = this.createCB(c);
7686         supr.setSize.call(this, w, h, a, d, cb, e);
7687         if(!a){
7688             cb();
7689         }
7690     },
7691
7692     // overridden Element method
7693     setWidth : function(w, a, d, c, e){
7694         this.beforeAction();
7695         var cb = this.createCB(c);
7696         supr.setWidth.call(this, w, a, d, cb, e);
7697         if(!a){
7698             cb();
7699         }
7700     },
7701
7702     // overridden Element method
7703     setHeight : function(h, a, d, c, e){
7704         this.beforeAction();
7705         var cb = this.createCB(c);
7706         supr.setHeight.call(this, h, a, d, cb, e);
7707         if(!a){
7708             cb();
7709         }
7710     },
7711
7712     // overridden Element method
7713     setBounds : function(x, y, w, h, a, d, c, e){
7714         this.beforeAction();
7715         var cb = this.createCB(c);
7716         if(!a){
7717             this.storeXY([x, y]);
7718             supr.setXY.call(this, [x, y]);
7719             supr.setSize.call(this, w, h, a, d, cb, e);
7720             cb();
7721         }else{
7722             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7723         }
7724         return this;
7725     },
7726     
7727     /**
7728      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7729      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7730      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7731      * @param {Number} zindex The new z-index to set
7732      * @return {this} The Layer
7733      */
7734     setZIndex : function(zindex){
7735         this.zindex = zindex;
7736         this.setStyle("z-index", zindex + 2);
7737         if(this.shadow){
7738             this.shadow.setZIndex(zindex + 1);
7739         }
7740         if(this.shim){
7741             this.shim.setStyle("z-index", zindex);
7742         }
7743     }
7744 });
7745 })();/*
7746  * Based on:
7747  * Ext JS Library 1.1.1
7748  * Copyright(c) 2006-2007, Ext JS, LLC.
7749  *
7750  * Originally Released Under LGPL - original licence link has changed is not relivant.
7751  *
7752  * Fork - LGPL
7753  * <script type="text/javascript">
7754  */
7755
7756
7757 /**
7758  * @class Roo.Shadow
7759  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7760  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7761  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7762  * @constructor
7763  * Create a new Shadow
7764  * @param {Object} config The config object
7765  */
7766 Roo.Shadow = function(config){
7767     Roo.apply(this, config);
7768     if(typeof this.mode != "string"){
7769         this.mode = this.defaultMode;
7770     }
7771     var o = this.offset, a = {h: 0};
7772     var rad = Math.floor(this.offset/2);
7773     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7774         case "drop":
7775             a.w = 0;
7776             a.l = a.t = o;
7777             a.t -= 1;
7778             if(Roo.isIE){
7779                 a.l -= this.offset + rad;
7780                 a.t -= this.offset + rad;
7781                 a.w -= rad;
7782                 a.h -= rad;
7783                 a.t += 1;
7784             }
7785         break;
7786         case "sides":
7787             a.w = (o*2);
7788             a.l = -o;
7789             a.t = o-1;
7790             if(Roo.isIE){
7791                 a.l -= (this.offset - rad);
7792                 a.t -= this.offset + rad;
7793                 a.l += 1;
7794                 a.w -= (this.offset - rad)*2;
7795                 a.w -= rad + 1;
7796                 a.h -= 1;
7797             }
7798         break;
7799         case "frame":
7800             a.w = a.h = (o*2);
7801             a.l = a.t = -o;
7802             a.t += 1;
7803             a.h -= 2;
7804             if(Roo.isIE){
7805                 a.l -= (this.offset - rad);
7806                 a.t -= (this.offset - rad);
7807                 a.l += 1;
7808                 a.w -= (this.offset + rad + 1);
7809                 a.h -= (this.offset + rad);
7810                 a.h += 1;
7811             }
7812         break;
7813     };
7814
7815     this.adjusts = a;
7816 };
7817
7818 Roo.Shadow.prototype = {
7819     /**
7820      * @cfg {String} mode
7821      * The shadow display mode.  Supports the following options:<br />
7822      * sides: Shadow displays on both sides and bottom only<br />
7823      * frame: Shadow displays equally on all four sides<br />
7824      * drop: Traditional bottom-right drop shadow (default)
7825      */
7826     /**
7827      * @cfg {String} offset
7828      * The number of pixels to offset the shadow from the element (defaults to 4)
7829      */
7830     offset: 4,
7831
7832     // private
7833     defaultMode: "drop",
7834
7835     /**
7836      * Displays the shadow under the target element
7837      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7838      */
7839     show : function(target){
7840         target = Roo.get(target);
7841         if(!this.el){
7842             this.el = Roo.Shadow.Pool.pull();
7843             if(this.el.dom.nextSibling != target.dom){
7844                 this.el.insertBefore(target);
7845             }
7846         }
7847         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7848         if(Roo.isIE){
7849             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7850         }
7851         this.realign(
7852             target.getLeft(true),
7853             target.getTop(true),
7854             target.getWidth(),
7855             target.getHeight()
7856         );
7857         this.el.dom.style.display = "block";
7858     },
7859
7860     /**
7861      * Returns true if the shadow is visible, else false
7862      */
7863     isVisible : function(){
7864         return this.el ? true : false;  
7865     },
7866
7867     /**
7868      * Direct alignment when values are already available. Show must be called at least once before
7869      * calling this method to ensure it is initialized.
7870      * @param {Number} left The target element left position
7871      * @param {Number} top The target element top position
7872      * @param {Number} width The target element width
7873      * @param {Number} height The target element height
7874      */
7875     realign : function(l, t, w, h){
7876         if(!this.el){
7877             return;
7878         }
7879         var a = this.adjusts, d = this.el.dom, s = d.style;
7880         var iea = 0;
7881         s.left = (l+a.l)+"px";
7882         s.top = (t+a.t)+"px";
7883         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7884  
7885         if(s.width != sws || s.height != shs){
7886             s.width = sws;
7887             s.height = shs;
7888             if(!Roo.isIE){
7889                 var cn = d.childNodes;
7890                 var sww = Math.max(0, (sw-12))+"px";
7891                 cn[0].childNodes[1].style.width = sww;
7892                 cn[1].childNodes[1].style.width = sww;
7893                 cn[2].childNodes[1].style.width = sww;
7894                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7895             }
7896         }
7897     },
7898
7899     /**
7900      * Hides this shadow
7901      */
7902     hide : function(){
7903         if(this.el){
7904             this.el.dom.style.display = "none";
7905             Roo.Shadow.Pool.push(this.el);
7906             delete this.el;
7907         }
7908     },
7909
7910     /**
7911      * Adjust the z-index of this shadow
7912      * @param {Number} zindex The new z-index
7913      */
7914     setZIndex : function(z){
7915         this.zIndex = z;
7916         if(this.el){
7917             this.el.setStyle("z-index", z);
7918         }
7919     }
7920 };
7921
7922 // Private utility class that manages the internal Shadow cache
7923 Roo.Shadow.Pool = function(){
7924     var p = [];
7925     var markup = Roo.isIE ?
7926                  '<div class="x-ie-shadow"></div>' :
7927                  '<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>';
7928     return {
7929         pull : function(){
7930             var sh = p.shift();
7931             if(!sh){
7932                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7933                 sh.autoBoxAdjust = false;
7934             }
7935             return sh;
7936         },
7937
7938         push : function(sh){
7939             p.push(sh);
7940         }
7941     };
7942 }();/*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952
7953
7954 /**
7955  * @class Roo.SplitBar
7956  * @extends Roo.util.Observable
7957  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7958  * <br><br>
7959  * Usage:
7960  * <pre><code>
7961 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7962                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7963 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7964 split.minSize = 100;
7965 split.maxSize = 600;
7966 split.animate = true;
7967 split.on('moved', splitterMoved);
7968 </code></pre>
7969  * @constructor
7970  * Create a new SplitBar
7971  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7972  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7973  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7974  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7975                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7976                         position of the SplitBar).
7977  */
7978 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7979     
7980     /** @private */
7981     this.el = Roo.get(dragElement, true);
7982     this.el.dom.unselectable = "on";
7983     /** @private */
7984     this.resizingEl = Roo.get(resizingElement, true);
7985
7986     /**
7987      * @private
7988      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7989      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7990      * @type Number
7991      */
7992     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7993     
7994     /**
7995      * The minimum size of the resizing element. (Defaults to 0)
7996      * @type Number
7997      */
7998     this.minSize = 0;
7999     
8000     /**
8001      * The maximum size of the resizing element. (Defaults to 2000)
8002      * @type Number
8003      */
8004     this.maxSize = 2000;
8005     
8006     /**
8007      * Whether to animate the transition to the new size
8008      * @type Boolean
8009      */
8010     this.animate = false;
8011     
8012     /**
8013      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8014      * @type Boolean
8015      */
8016     this.useShim = false;
8017     
8018     /** @private */
8019     this.shim = null;
8020     
8021     if(!existingProxy){
8022         /** @private */
8023         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8024     }else{
8025         this.proxy = Roo.get(existingProxy).dom;
8026     }
8027     /** @private */
8028     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8029     
8030     /** @private */
8031     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8032     
8033     /** @private */
8034     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8035     
8036     /** @private */
8037     this.dragSpecs = {};
8038     
8039     /**
8040      * @private The adapter to use to positon and resize elements
8041      */
8042     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8043     this.adapter.init(this);
8044     
8045     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8046         /** @private */
8047         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8048         this.el.addClass("x-splitbar-h");
8049     }else{
8050         /** @private */
8051         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8052         this.el.addClass("x-splitbar-v");
8053     }
8054     
8055     this.addEvents({
8056         /**
8057          * @event resize
8058          * Fires when the splitter is moved (alias for {@link #event-moved})
8059          * @param {Roo.SplitBar} this
8060          * @param {Number} newSize the new width or height
8061          */
8062         "resize" : true,
8063         /**
8064          * @event moved
8065          * Fires when the splitter is moved
8066          * @param {Roo.SplitBar} this
8067          * @param {Number} newSize the new width or height
8068          */
8069         "moved" : true,
8070         /**
8071          * @event beforeresize
8072          * Fires before the splitter is dragged
8073          * @param {Roo.SplitBar} this
8074          */
8075         "beforeresize" : true,
8076
8077         "beforeapply" : true
8078     });
8079
8080     Roo.util.Observable.call(this);
8081 };
8082
8083 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8084     onStartProxyDrag : function(x, y){
8085         this.fireEvent("beforeresize", this);
8086         if(!this.overlay){
8087             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8088             o.unselectable();
8089             o.enableDisplayMode("block");
8090             // all splitbars share the same overlay
8091             Roo.SplitBar.prototype.overlay = o;
8092         }
8093         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8094         this.overlay.show();
8095         Roo.get(this.proxy).setDisplayed("block");
8096         var size = this.adapter.getElementSize(this);
8097         this.activeMinSize = this.getMinimumSize();;
8098         this.activeMaxSize = this.getMaximumSize();;
8099         var c1 = size - this.activeMinSize;
8100         var c2 = Math.max(this.activeMaxSize - size, 0);
8101         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8102             this.dd.resetConstraints();
8103             this.dd.setXConstraint(
8104                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8105                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8106             );
8107             this.dd.setYConstraint(0, 0);
8108         }else{
8109             this.dd.resetConstraints();
8110             this.dd.setXConstraint(0, 0);
8111             this.dd.setYConstraint(
8112                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8113                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8114             );
8115          }
8116         this.dragSpecs.startSize = size;
8117         this.dragSpecs.startPoint = [x, y];
8118         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8119     },
8120     
8121     /** 
8122      * @private Called after the drag operation by the DDProxy
8123      */
8124     onEndProxyDrag : function(e){
8125         Roo.get(this.proxy).setDisplayed(false);
8126         var endPoint = Roo.lib.Event.getXY(e);
8127         if(this.overlay){
8128             this.overlay.hide();
8129         }
8130         var newSize;
8131         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8132             newSize = this.dragSpecs.startSize + 
8133                 (this.placement == Roo.SplitBar.LEFT ?
8134                     endPoint[0] - this.dragSpecs.startPoint[0] :
8135                     this.dragSpecs.startPoint[0] - endPoint[0]
8136                 );
8137         }else{
8138             newSize = this.dragSpecs.startSize + 
8139                 (this.placement == Roo.SplitBar.TOP ?
8140                     endPoint[1] - this.dragSpecs.startPoint[1] :
8141                     this.dragSpecs.startPoint[1] - endPoint[1]
8142                 );
8143         }
8144         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8145         if(newSize != this.dragSpecs.startSize){
8146             if(this.fireEvent('beforeapply', this, newSize) !== false){
8147                 this.adapter.setElementSize(this, newSize);
8148                 this.fireEvent("moved", this, newSize);
8149                 this.fireEvent("resize", this, newSize);
8150             }
8151         }
8152     },
8153     
8154     /**
8155      * Get the adapter this SplitBar uses
8156      * @return The adapter object
8157      */
8158     getAdapter : function(){
8159         return this.adapter;
8160     },
8161     
8162     /**
8163      * Set the adapter this SplitBar uses
8164      * @param {Object} adapter A SplitBar adapter object
8165      */
8166     setAdapter : function(adapter){
8167         this.adapter = adapter;
8168         this.adapter.init(this);
8169     },
8170     
8171     /**
8172      * Gets the minimum size for the resizing element
8173      * @return {Number} The minimum size
8174      */
8175     getMinimumSize : function(){
8176         return this.minSize;
8177     },
8178     
8179     /**
8180      * Sets the minimum size for the resizing element
8181      * @param {Number} minSize The minimum size
8182      */
8183     setMinimumSize : function(minSize){
8184         this.minSize = minSize;
8185     },
8186     
8187     /**
8188      * Gets the maximum size for the resizing element
8189      * @return {Number} The maximum size
8190      */
8191     getMaximumSize : function(){
8192         return this.maxSize;
8193     },
8194     
8195     /**
8196      * Sets the maximum size for the resizing element
8197      * @param {Number} maxSize The maximum size
8198      */
8199     setMaximumSize : function(maxSize){
8200         this.maxSize = maxSize;
8201     },
8202     
8203     /**
8204      * Sets the initialize size for the resizing element
8205      * @param {Number} size The initial size
8206      */
8207     setCurrentSize : function(size){
8208         var oldAnimate = this.animate;
8209         this.animate = false;
8210         this.adapter.setElementSize(this, size);
8211         this.animate = oldAnimate;
8212     },
8213     
8214     /**
8215      * Destroy this splitbar. 
8216      * @param {Boolean} removeEl True to remove the element
8217      */
8218     destroy : function(removeEl){
8219         if(this.shim){
8220             this.shim.remove();
8221         }
8222         this.dd.unreg();
8223         this.proxy.parentNode.removeChild(this.proxy);
8224         if(removeEl){
8225             this.el.remove();
8226         }
8227     }
8228 });
8229
8230 /**
8231  * @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.
8232  */
8233 Roo.SplitBar.createProxy = function(dir){
8234     var proxy = new Roo.Element(document.createElement("div"));
8235     proxy.unselectable();
8236     var cls = 'x-splitbar-proxy';
8237     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8238     document.body.appendChild(proxy.dom);
8239     return proxy.dom;
8240 };
8241
8242 /** 
8243  * @class Roo.SplitBar.BasicLayoutAdapter
8244  * Default Adapter. It assumes the splitter and resizing element are not positioned
8245  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8246  */
8247 Roo.SplitBar.BasicLayoutAdapter = function(){
8248 };
8249
8250 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8251     // do nothing for now
8252     init : function(s){
8253     
8254     },
8255     /**
8256      * Called before drag operations to get the current size of the resizing element. 
8257      * @param {Roo.SplitBar} s The SplitBar using this adapter
8258      */
8259      getElementSize : function(s){
8260         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8261             return s.resizingEl.getWidth();
8262         }else{
8263             return s.resizingEl.getHeight();
8264         }
8265     },
8266     
8267     /**
8268      * Called after drag operations to set the size of the resizing element.
8269      * @param {Roo.SplitBar} s The SplitBar using this adapter
8270      * @param {Number} newSize The new size to set
8271      * @param {Function} onComplete A function to be invoked when resizing is complete
8272      */
8273     setElementSize : function(s, newSize, onComplete){
8274         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8275             if(!s.animate){
8276                 s.resizingEl.setWidth(newSize);
8277                 if(onComplete){
8278                     onComplete(s, newSize);
8279                 }
8280             }else{
8281                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8282             }
8283         }else{
8284             
8285             if(!s.animate){
8286                 s.resizingEl.setHeight(newSize);
8287                 if(onComplete){
8288                     onComplete(s, newSize);
8289                 }
8290             }else{
8291                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8292             }
8293         }
8294     }
8295 };
8296
8297 /** 
8298  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8299  * @extends Roo.SplitBar.BasicLayoutAdapter
8300  * Adapter that  moves the splitter element to align with the resized sizing element. 
8301  * Used with an absolute positioned SplitBar.
8302  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8303  * document.body, make sure you assign an id to the body element.
8304  */
8305 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8306     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8307     this.container = Roo.get(container);
8308 };
8309
8310 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8311     init : function(s){
8312         this.basic.init(s);
8313     },
8314     
8315     getElementSize : function(s){
8316         return this.basic.getElementSize(s);
8317     },
8318     
8319     setElementSize : function(s, newSize, onComplete){
8320         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8321     },
8322     
8323     moveSplitter : function(s){
8324         var yes = Roo.SplitBar;
8325         switch(s.placement){
8326             case yes.LEFT:
8327                 s.el.setX(s.resizingEl.getRight());
8328                 break;
8329             case yes.RIGHT:
8330                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8331                 break;
8332             case yes.TOP:
8333                 s.el.setY(s.resizingEl.getBottom());
8334                 break;
8335             case yes.BOTTOM:
8336                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8337                 break;
8338         }
8339     }
8340 };
8341
8342 /**
8343  * Orientation constant - Create a vertical SplitBar
8344  * @static
8345  * @type Number
8346  */
8347 Roo.SplitBar.VERTICAL = 1;
8348
8349 /**
8350  * Orientation constant - Create a horizontal SplitBar
8351  * @static
8352  * @type Number
8353  */
8354 Roo.SplitBar.HORIZONTAL = 2;
8355
8356 /**
8357  * Placement constant - The resizing element is to the left of the splitter element
8358  * @static
8359  * @type Number
8360  */
8361 Roo.SplitBar.LEFT = 1;
8362
8363 /**
8364  * Placement constant - The resizing element is to the right of the splitter element
8365  * @static
8366  * @type Number
8367  */
8368 Roo.SplitBar.RIGHT = 2;
8369
8370 /**
8371  * Placement constant - The resizing element is positioned above the splitter element
8372  * @static
8373  * @type Number
8374  */
8375 Roo.SplitBar.TOP = 3;
8376
8377 /**
8378  * Placement constant - The resizing element is positioned under splitter element
8379  * @static
8380  * @type Number
8381  */
8382 Roo.SplitBar.BOTTOM = 4;
8383 /*
8384  * Based on:
8385  * Ext JS Library 1.1.1
8386  * Copyright(c) 2006-2007, Ext JS, LLC.
8387  *
8388  * Originally Released Under LGPL - original licence link has changed is not relivant.
8389  *
8390  * Fork - LGPL
8391  * <script type="text/javascript">
8392  */
8393
8394 /**
8395  * @class Roo.View
8396  * @extends Roo.util.Observable
8397  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8398  * This class also supports single and multi selection modes. <br>
8399  * Create a data model bound view:
8400  <pre><code>
8401  var store = new Roo.data.Store(...);
8402
8403  var view = new Roo.View({
8404     el : "my-element",
8405     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8406  
8407     singleSelect: true,
8408     selectedClass: "ydataview-selected",
8409     store: store
8410  });
8411
8412  // listen for node click?
8413  view.on("click", function(vw, index, node, e){
8414  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8415  });
8416
8417  // load XML data
8418  dataModel.load("foobar.xml");
8419  </code></pre>
8420  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8421  * <br><br>
8422  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8423  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8424  * 
8425  * Note: old style constructor is still suported (container, template, config)
8426  * 
8427  * @constructor
8428  * Create a new View
8429  * @param {Object} config The config object
8430  * 
8431  */
8432 Roo.View = function(config, depreciated_tpl, depreciated_config){
8433     
8434     this.parent = false;
8435     
8436     if (typeof(depreciated_tpl) == 'undefined') {
8437         // new way.. - universal constructor.
8438         Roo.apply(this, config);
8439         this.el  = Roo.get(this.el);
8440     } else {
8441         // old format..
8442         this.el  = Roo.get(config);
8443         this.tpl = depreciated_tpl;
8444         Roo.apply(this, depreciated_config);
8445     }
8446     this.wrapEl  = this.el.wrap().wrap();
8447     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8448     
8449     
8450     if(typeof(this.tpl) == "string"){
8451         this.tpl = new Roo.Template(this.tpl);
8452     } else {
8453         // support xtype ctors..
8454         this.tpl = new Roo.factory(this.tpl, Roo);
8455     }
8456     
8457     
8458     this.tpl.compile();
8459     
8460     /** @private */
8461     this.addEvents({
8462         /**
8463          * @event beforeclick
8464          * Fires before a click is processed. Returns false to cancel the default action.
8465          * @param {Roo.View} this
8466          * @param {Number} index The index of the target node
8467          * @param {HTMLElement} node The target node
8468          * @param {Roo.EventObject} e The raw event object
8469          */
8470             "beforeclick" : true,
8471         /**
8472          * @event click
8473          * Fires when a template node is clicked.
8474          * @param {Roo.View} this
8475          * @param {Number} index The index of the target node
8476          * @param {HTMLElement} node The target node
8477          * @param {Roo.EventObject} e The raw event object
8478          */
8479             "click" : true,
8480         /**
8481          * @event dblclick
8482          * Fires when a template node is double clicked.
8483          * @param {Roo.View} this
8484          * @param {Number} index The index of the target node
8485          * @param {HTMLElement} node The target node
8486          * @param {Roo.EventObject} e The raw event object
8487          */
8488             "dblclick" : true,
8489         /**
8490          * @event contextmenu
8491          * Fires when a template node is right clicked.
8492          * @param {Roo.View} this
8493          * @param {Number} index The index of the target node
8494          * @param {HTMLElement} node The target node
8495          * @param {Roo.EventObject} e The raw event object
8496          */
8497             "contextmenu" : true,
8498         /**
8499          * @event selectionchange
8500          * Fires when the selected nodes change.
8501          * @param {Roo.View} this
8502          * @param {Array} selections Array of the selected nodes
8503          */
8504             "selectionchange" : true,
8505     
8506         /**
8507          * @event beforeselect
8508          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8509          * @param {Roo.View} this
8510          * @param {HTMLElement} node The node to be selected
8511          * @param {Array} selections Array of currently selected nodes
8512          */
8513             "beforeselect" : true,
8514         /**
8515          * @event preparedata
8516          * Fires on every row to render, to allow you to change the data.
8517          * @param {Roo.View} this
8518          * @param {Object} data to be rendered (change this)
8519          */
8520           "preparedata" : true
8521           
8522           
8523         });
8524
8525
8526
8527     this.el.on({
8528         "click": this.onClick,
8529         "dblclick": this.onDblClick,
8530         "contextmenu": this.onContextMenu,
8531         scope:this
8532     });
8533
8534     this.selections = [];
8535     this.nodes = [];
8536     this.cmp = new Roo.CompositeElementLite([]);
8537     if(this.store){
8538         this.store = Roo.factory(this.store, Roo.data);
8539         this.setStore(this.store, true);
8540     }
8541     
8542     if ( this.footer && this.footer.xtype) {
8543            
8544          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8545         
8546         this.footer.dataSource = this.store
8547         this.footer.container = fctr;
8548         this.footer = Roo.factory(this.footer, Roo);
8549         fctr.insertFirst(this.el);
8550         
8551         // this is a bit insane - as the paging toolbar seems to detach the el..
8552 //        dom.parentNode.parentNode.parentNode
8553          // they get detached?
8554     }
8555     
8556     
8557     Roo.View.superclass.constructor.call(this);
8558     
8559     
8560 };
8561
8562 Roo.extend(Roo.View, Roo.util.Observable, {
8563     
8564      /**
8565      * @cfg {Roo.data.Store} store Data store to load data from.
8566      */
8567     store : false,
8568     
8569     /**
8570      * @cfg {String|Roo.Element} el The container element.
8571      */
8572     el : '',
8573     
8574     /**
8575      * @cfg {String|Roo.Template} tpl The template used by this View 
8576      */
8577     tpl : false,
8578     /**
8579      * @cfg {String} dataName the named area of the template to use as the data area
8580      *                          Works with domtemplates roo-name="name"
8581      */
8582     dataName: false,
8583     /**
8584      * @cfg {String} selectedClass The css class to add to selected nodes
8585      */
8586     selectedClass : "x-view-selected",
8587      /**
8588      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8589      */
8590     emptyText : "",
8591     
8592     /**
8593      * @cfg {String} text to display on mask (default Loading)
8594      */
8595     mask : false,
8596     /**
8597      * @cfg {Boolean} multiSelect Allow multiple selection
8598      */
8599     multiSelect : false,
8600     /**
8601      * @cfg {Boolean} singleSelect Allow single selection
8602      */
8603     singleSelect:  false,
8604     
8605     /**
8606      * @cfg {Boolean} toggleSelect - selecting 
8607      */
8608     toggleSelect : false,
8609     
8610     /**
8611      * @cfg {Boolean} tickable - selecting 
8612      */
8613     tickable : false,
8614     
8615     /**
8616      * Returns the element this view is bound to.
8617      * @return {Roo.Element}
8618      */
8619     getEl : function(){
8620         return this.wrapEl;
8621     },
8622     
8623     
8624
8625     /**
8626      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8627      */
8628     refresh : function(){
8629         //Roo.log('refresh');
8630         var t = this.tpl;
8631         
8632         // if we are using something like 'domtemplate', then
8633         // the what gets used is:
8634         // t.applySubtemplate(NAME, data, wrapping data..)
8635         // the outer template then get' applied with
8636         //     the store 'extra data'
8637         // and the body get's added to the
8638         //      roo-name="data" node?
8639         //      <span class='roo-tpl-{name}'></span> ?????
8640         
8641         
8642         
8643         this.clearSelections();
8644         this.el.update("");
8645         var html = [];
8646         var records = this.store.getRange();
8647         if(records.length < 1) {
8648             
8649             // is this valid??  = should it render a template??
8650             
8651             this.el.update(this.emptyText);
8652             return;
8653         }
8654         var el = this.el;
8655         if (this.dataName) {
8656             this.el.update(t.apply(this.store.meta)); //????
8657             el = this.el.child('.roo-tpl-' + this.dataName);
8658         }
8659         
8660         for(var i = 0, len = records.length; i < len; i++){
8661             var data = this.prepareData(records[i].data, i, records[i]);
8662             this.fireEvent("preparedata", this, data, i, records[i]);
8663             
8664             var d = Roo.apply({}, data);
8665             
8666             if(this.tickable){
8667                 Roo.apply(d, {'roo-id' : Roo.id()});
8668                 
8669                 var _this = this;
8670             
8671                 Roo.each(this.parent.item, function(item){
8672                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8673                         return;
8674                     }
8675                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8676                 });
8677             }
8678             
8679             html[html.length] = Roo.util.Format.trim(
8680                 this.dataName ?
8681                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8682                     t.apply(d)
8683             );
8684         }
8685         
8686         
8687         
8688         el.update(html.join(""));
8689         this.nodes = el.dom.childNodes;
8690         this.updateIndexes(0);
8691     },
8692     
8693
8694     /**
8695      * Function to override to reformat the data that is sent to
8696      * the template for each node.
8697      * DEPRICATED - use the preparedata event handler.
8698      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8699      * a JSON object for an UpdateManager bound view).
8700      */
8701     prepareData : function(data, index, record)
8702     {
8703         this.fireEvent("preparedata", this, data, index, record);
8704         return data;
8705     },
8706
8707     onUpdate : function(ds, record){
8708         // Roo.log('on update');   
8709         this.clearSelections();
8710         var index = this.store.indexOf(record);
8711         var n = this.nodes[index];
8712         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8713         n.parentNode.removeChild(n);
8714         this.updateIndexes(index, index);
8715     },
8716
8717     
8718     
8719 // --------- FIXME     
8720     onAdd : function(ds, records, index)
8721     {
8722         //Roo.log(['on Add', ds, records, index] );        
8723         this.clearSelections();
8724         if(this.nodes.length == 0){
8725             this.refresh();
8726             return;
8727         }
8728         var n = this.nodes[index];
8729         for(var i = 0, len = records.length; i < len; i++){
8730             var d = this.prepareData(records[i].data, i, records[i]);
8731             if(n){
8732                 this.tpl.insertBefore(n, d);
8733             }else{
8734                 
8735                 this.tpl.append(this.el, d);
8736             }
8737         }
8738         this.updateIndexes(index);
8739     },
8740
8741     onRemove : function(ds, record, index){
8742        // Roo.log('onRemove');
8743         this.clearSelections();
8744         var el = this.dataName  ?
8745             this.el.child('.roo-tpl-' + this.dataName) :
8746             this.el; 
8747         
8748         el.dom.removeChild(this.nodes[index]);
8749         this.updateIndexes(index);
8750     },
8751
8752     /**
8753      * Refresh an individual node.
8754      * @param {Number} index
8755      */
8756     refreshNode : function(index){
8757         this.onUpdate(this.store, this.store.getAt(index));
8758     },
8759
8760     updateIndexes : function(startIndex, endIndex){
8761         var ns = this.nodes;
8762         startIndex = startIndex || 0;
8763         endIndex = endIndex || ns.length - 1;
8764         for(var i = startIndex; i <= endIndex; i++){
8765             ns[i].nodeIndex = i;
8766         }
8767     },
8768
8769     /**
8770      * Changes the data store this view uses and refresh the view.
8771      * @param {Store} store
8772      */
8773     setStore : function(store, initial){
8774         if(!initial && this.store){
8775             this.store.un("datachanged", this.refresh);
8776             this.store.un("add", this.onAdd);
8777             this.store.un("remove", this.onRemove);
8778             this.store.un("update", this.onUpdate);
8779             this.store.un("clear", this.refresh);
8780             this.store.un("beforeload", this.onBeforeLoad);
8781             this.store.un("load", this.onLoad);
8782             this.store.un("loadexception", this.onLoad);
8783         }
8784         if(store){
8785           
8786             store.on("datachanged", this.refresh, this);
8787             store.on("add", this.onAdd, this);
8788             store.on("remove", this.onRemove, this);
8789             store.on("update", this.onUpdate, this);
8790             store.on("clear", this.refresh, this);
8791             store.on("beforeload", this.onBeforeLoad, this);
8792             store.on("load", this.onLoad, this);
8793             store.on("loadexception", this.onLoad, this);
8794         }
8795         
8796         if(store){
8797             this.refresh();
8798         }
8799     },
8800     /**
8801      * onbeforeLoad - masks the loading area.
8802      *
8803      */
8804     onBeforeLoad : function(store,opts)
8805     {
8806          //Roo.log('onBeforeLoad');   
8807         if (!opts.add) {
8808             this.el.update("");
8809         }
8810         this.el.mask(this.mask ? this.mask : "Loading" ); 
8811     },
8812     onLoad : function ()
8813     {
8814         this.el.unmask();
8815     },
8816     
8817
8818     /**
8819      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8820      * @param {HTMLElement} node
8821      * @return {HTMLElement} The template node
8822      */
8823     findItemFromChild : function(node){
8824         var el = this.dataName  ?
8825             this.el.child('.roo-tpl-' + this.dataName,true) :
8826             this.el.dom; 
8827         
8828         if(!node || node.parentNode == el){
8829                     return node;
8830             }
8831             var p = node.parentNode;
8832             while(p && p != el){
8833             if(p.parentNode == el){
8834                 return p;
8835             }
8836             p = p.parentNode;
8837         }
8838             return null;
8839     },
8840
8841     /** @ignore */
8842     onClick : function(e){
8843         var item = this.findItemFromChild(e.getTarget());
8844         if(item){
8845             var index = this.indexOf(item);
8846             if(this.onItemClick(item, index, e) !== false){
8847                 this.fireEvent("click", this, index, item, e);
8848             }
8849         }else{
8850             this.clearSelections();
8851         }
8852     },
8853
8854     /** @ignore */
8855     onContextMenu : function(e){
8856         var item = this.findItemFromChild(e.getTarget());
8857         if(item){
8858             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8859         }
8860     },
8861
8862     /** @ignore */
8863     onDblClick : function(e){
8864         var item = this.findItemFromChild(e.getTarget());
8865         if(item){
8866             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8867         }
8868     },
8869
8870     onItemClick : function(item, index, e)
8871     {
8872         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8873             return false;
8874         }
8875         if (this.toggleSelect) {
8876             var m = this.isSelected(item) ? 'unselect' : 'select';
8877             //Roo.log(m);
8878             var _t = this;
8879             _t[m](item, true, false);
8880             return true;
8881         }
8882         if(this.multiSelect || this.singleSelect){
8883             if(this.multiSelect && e.shiftKey && this.lastSelection){
8884                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8885             }else{
8886                 this.select(item, this.multiSelect && e.ctrlKey);
8887                 this.lastSelection = item;
8888             }
8889             
8890             if(!this.tickable){
8891                 e.preventDefault();
8892             }
8893             
8894         }
8895         return true;
8896     },
8897
8898     /**
8899      * Get the number of selected nodes.
8900      * @return {Number}
8901      */
8902     getSelectionCount : function(){
8903         return this.selections.length;
8904     },
8905
8906     /**
8907      * Get the currently selected nodes.
8908      * @return {Array} An array of HTMLElements
8909      */
8910     getSelectedNodes : function(){
8911         return this.selections;
8912     },
8913
8914     /**
8915      * Get the indexes of the selected nodes.
8916      * @return {Array}
8917      */
8918     getSelectedIndexes : function(){
8919         var indexes = [], s = this.selections;
8920         for(var i = 0, len = s.length; i < len; i++){
8921             indexes.push(s[i].nodeIndex);
8922         }
8923         return indexes;
8924     },
8925
8926     /**
8927      * Clear all selections
8928      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8929      */
8930     clearSelections : function(suppressEvent){
8931         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8932             this.cmp.elements = this.selections;
8933             this.cmp.removeClass(this.selectedClass);
8934             this.selections = [];
8935             if(!suppressEvent){
8936                 this.fireEvent("selectionchange", this, this.selections);
8937             }
8938         }
8939     },
8940
8941     /**
8942      * Returns true if the passed node is selected
8943      * @param {HTMLElement/Number} node The node or node index
8944      * @return {Boolean}
8945      */
8946     isSelected : function(node){
8947         var s = this.selections;
8948         if(s.length < 1){
8949             return false;
8950         }
8951         node = this.getNode(node);
8952         return s.indexOf(node) !== -1;
8953     },
8954
8955     /**
8956      * Selects nodes.
8957      * @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
8958      * @param {Boolean} keepExisting (optional) true to keep existing selections
8959      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8960      */
8961     select : function(nodeInfo, keepExisting, suppressEvent){
8962         if(nodeInfo instanceof Array){
8963             if(!keepExisting){
8964                 this.clearSelections(true);
8965             }
8966             for(var i = 0, len = nodeInfo.length; i < len; i++){
8967                 this.select(nodeInfo[i], true, true);
8968             }
8969             return;
8970         } 
8971         var node = this.getNode(nodeInfo);
8972         if(!node || this.isSelected(node)){
8973             return; // already selected.
8974         }
8975         if(!keepExisting){
8976             this.clearSelections(true);
8977         }
8978         
8979         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8980             Roo.fly(node).addClass(this.selectedClass);
8981             this.selections.push(node);
8982             if(!suppressEvent){
8983                 this.fireEvent("selectionchange", this, this.selections);
8984             }
8985         }
8986         
8987         
8988     },
8989       /**
8990      * Unselects nodes.
8991      * @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
8992      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8993      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8994      */
8995     unselect : function(nodeInfo, keepExisting, suppressEvent)
8996     {
8997         if(nodeInfo instanceof Array){
8998             Roo.each(this.selections, function(s) {
8999                 this.unselect(s, nodeInfo);
9000             }, this);
9001             return;
9002         }
9003         var node = this.getNode(nodeInfo);
9004         if(!node || !this.isSelected(node)){
9005             //Roo.log("not selected");
9006             return; // not selected.
9007         }
9008         // fireevent???
9009         var ns = [];
9010         Roo.each(this.selections, function(s) {
9011             if (s == node ) {
9012                 Roo.fly(node).removeClass(this.selectedClass);
9013
9014                 return;
9015             }
9016             ns.push(s);
9017         },this);
9018         
9019         this.selections= ns;
9020         this.fireEvent("selectionchange", this, this.selections);
9021     },
9022
9023     /**
9024      * Gets a template node.
9025      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9026      * @return {HTMLElement} The node or null if it wasn't found
9027      */
9028     getNode : function(nodeInfo){
9029         if(typeof nodeInfo == "string"){
9030             return document.getElementById(nodeInfo);
9031         }else if(typeof nodeInfo == "number"){
9032             return this.nodes[nodeInfo];
9033         }
9034         return nodeInfo;
9035     },
9036
9037     /**
9038      * Gets a range template nodes.
9039      * @param {Number} startIndex
9040      * @param {Number} endIndex
9041      * @return {Array} An array of nodes
9042      */
9043     getNodes : function(start, end){
9044         var ns = this.nodes;
9045         start = start || 0;
9046         end = typeof end == "undefined" ? ns.length - 1 : end;
9047         var nodes = [];
9048         if(start <= end){
9049             for(var i = start; i <= end; i++){
9050                 nodes.push(ns[i]);
9051             }
9052         } else{
9053             for(var i = start; i >= end; i--){
9054                 nodes.push(ns[i]);
9055             }
9056         }
9057         return nodes;
9058     },
9059
9060     /**
9061      * Finds the index of the passed node
9062      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9063      * @return {Number} The index of the node or -1
9064      */
9065     indexOf : function(node){
9066         node = this.getNode(node);
9067         if(typeof node.nodeIndex == "number"){
9068             return node.nodeIndex;
9069         }
9070         var ns = this.nodes;
9071         for(var i = 0, len = ns.length; i < len; i++){
9072             if(ns[i] == node){
9073                 return i;
9074             }
9075         }
9076         return -1;
9077     }
9078 });
9079 /*
9080  * Based on:
9081  * Ext JS Library 1.1.1
9082  * Copyright(c) 2006-2007, Ext JS, LLC.
9083  *
9084  * Originally Released Under LGPL - original licence link has changed is not relivant.
9085  *
9086  * Fork - LGPL
9087  * <script type="text/javascript">
9088  */
9089
9090 /**
9091  * @class Roo.JsonView
9092  * @extends Roo.View
9093  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9094 <pre><code>
9095 var view = new Roo.JsonView({
9096     container: "my-element",
9097     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9098     multiSelect: true, 
9099     jsonRoot: "data" 
9100 });
9101
9102 // listen for node click?
9103 view.on("click", function(vw, index, node, e){
9104     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9105 });
9106
9107 // direct load of JSON data
9108 view.load("foobar.php");
9109
9110 // Example from my blog list
9111 var tpl = new Roo.Template(
9112     '&lt;div class="entry"&gt;' +
9113     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9114     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9115     "&lt;/div&gt;&lt;hr /&gt;"
9116 );
9117
9118 var moreView = new Roo.JsonView({
9119     container :  "entry-list", 
9120     template : tpl,
9121     jsonRoot: "posts"
9122 });
9123 moreView.on("beforerender", this.sortEntries, this);
9124 moreView.load({
9125     url: "/blog/get-posts.php",
9126     params: "allposts=true",
9127     text: "Loading Blog Entries..."
9128 });
9129 </code></pre>
9130
9131 * Note: old code is supported with arguments : (container, template, config)
9132
9133
9134  * @constructor
9135  * Create a new JsonView
9136  * 
9137  * @param {Object} config The config object
9138  * 
9139  */
9140 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9141     
9142     
9143     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9144
9145     var um = this.el.getUpdateManager();
9146     um.setRenderer(this);
9147     um.on("update", this.onLoad, this);
9148     um.on("failure", this.onLoadException, this);
9149
9150     /**
9151      * @event beforerender
9152      * Fires before rendering of the downloaded JSON data.
9153      * @param {Roo.JsonView} this
9154      * @param {Object} data The JSON data loaded
9155      */
9156     /**
9157      * @event load
9158      * Fires when data is loaded.
9159      * @param {Roo.JsonView} this
9160      * @param {Object} data The JSON data loaded
9161      * @param {Object} response The raw Connect response object
9162      */
9163     /**
9164      * @event loadexception
9165      * Fires when loading fails.
9166      * @param {Roo.JsonView} this
9167      * @param {Object} response The raw Connect response object
9168      */
9169     this.addEvents({
9170         'beforerender' : true,
9171         'load' : true,
9172         'loadexception' : true
9173     });
9174 };
9175 Roo.extend(Roo.JsonView, Roo.View, {
9176     /**
9177      * @type {String} The root property in the loaded JSON object that contains the data
9178      */
9179     jsonRoot : "",
9180
9181     /**
9182      * Refreshes the view.
9183      */
9184     refresh : function(){
9185         this.clearSelections();
9186         this.el.update("");
9187         var html = [];
9188         var o = this.jsonData;
9189         if(o && o.length > 0){
9190             for(var i = 0, len = o.length; i < len; i++){
9191                 var data = this.prepareData(o[i], i, o);
9192                 html[html.length] = this.tpl.apply(data);
9193             }
9194         }else{
9195             html.push(this.emptyText);
9196         }
9197         this.el.update(html.join(""));
9198         this.nodes = this.el.dom.childNodes;
9199         this.updateIndexes(0);
9200     },
9201
9202     /**
9203      * 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.
9204      * @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:
9205      <pre><code>
9206      view.load({
9207          url: "your-url.php",
9208          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9209          callback: yourFunction,
9210          scope: yourObject, //(optional scope)
9211          discardUrl: false,
9212          nocache: false,
9213          text: "Loading...",
9214          timeout: 30,
9215          scripts: false
9216      });
9217      </code></pre>
9218      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9219      * 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.
9220      * @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}
9221      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9222      * @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.
9223      */
9224     load : function(){
9225         var um = this.el.getUpdateManager();
9226         um.update.apply(um, arguments);
9227     },
9228
9229     render : function(el, response){
9230         this.clearSelections();
9231         this.el.update("");
9232         var o;
9233         try{
9234             o = Roo.util.JSON.decode(response.responseText);
9235             if(this.jsonRoot){
9236                 
9237                 o = o[this.jsonRoot];
9238             }
9239         } catch(e){
9240         }
9241         /**
9242          * The current JSON data or null
9243          */
9244         this.jsonData = o;
9245         this.beforeRender();
9246         this.refresh();
9247     },
9248
9249 /**
9250  * Get the number of records in the current JSON dataset
9251  * @return {Number}
9252  */
9253     getCount : function(){
9254         return this.jsonData ? this.jsonData.length : 0;
9255     },
9256
9257 /**
9258  * Returns the JSON object for the specified node(s)
9259  * @param {HTMLElement/Array} node The node or an array of nodes
9260  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9261  * you get the JSON object for the node
9262  */
9263     getNodeData : function(node){
9264         if(node instanceof Array){
9265             var data = [];
9266             for(var i = 0, len = node.length; i < len; i++){
9267                 data.push(this.getNodeData(node[i]));
9268             }
9269             return data;
9270         }
9271         return this.jsonData[this.indexOf(node)] || null;
9272     },
9273
9274     beforeRender : function(){
9275         this.snapshot = this.jsonData;
9276         if(this.sortInfo){
9277             this.sort.apply(this, this.sortInfo);
9278         }
9279         this.fireEvent("beforerender", this, this.jsonData);
9280     },
9281
9282     onLoad : function(el, o){
9283         this.fireEvent("load", this, this.jsonData, o);
9284     },
9285
9286     onLoadException : function(el, o){
9287         this.fireEvent("loadexception", this, o);
9288     },
9289
9290 /**
9291  * Filter the data by a specific property.
9292  * @param {String} property A property on your JSON objects
9293  * @param {String/RegExp} value Either string that the property values
9294  * should start with, or a RegExp to test against the property
9295  */
9296     filter : function(property, value){
9297         if(this.jsonData){
9298             var data = [];
9299             var ss = this.snapshot;
9300             if(typeof value == "string"){
9301                 var vlen = value.length;
9302                 if(vlen == 0){
9303                     this.clearFilter();
9304                     return;
9305                 }
9306                 value = value.toLowerCase();
9307                 for(var i = 0, len = ss.length; i < len; i++){
9308                     var o = ss[i];
9309                     if(o[property].substr(0, vlen).toLowerCase() == value){
9310                         data.push(o);
9311                     }
9312                 }
9313             } else if(value.exec){ // regex?
9314                 for(var i = 0, len = ss.length; i < len; i++){
9315                     var o = ss[i];
9316                     if(value.test(o[property])){
9317                         data.push(o);
9318                     }
9319                 }
9320             } else{
9321                 return;
9322             }
9323             this.jsonData = data;
9324             this.refresh();
9325         }
9326     },
9327
9328 /**
9329  * Filter by a function. The passed function will be called with each
9330  * object in the current dataset. If the function returns true the value is kept,
9331  * otherwise it is filtered.
9332  * @param {Function} fn
9333  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9334  */
9335     filterBy : function(fn, scope){
9336         if(this.jsonData){
9337             var data = [];
9338             var ss = this.snapshot;
9339             for(var i = 0, len = ss.length; i < len; i++){
9340                 var o = ss[i];
9341                 if(fn.call(scope || this, o)){
9342                     data.push(o);
9343                 }
9344             }
9345             this.jsonData = data;
9346             this.refresh();
9347         }
9348     },
9349
9350 /**
9351  * Clears the current filter.
9352  */
9353     clearFilter : function(){
9354         if(this.snapshot && this.jsonData != this.snapshot){
9355             this.jsonData = this.snapshot;
9356             this.refresh();
9357         }
9358     },
9359
9360
9361 /**
9362  * Sorts the data for this view and refreshes it.
9363  * @param {String} property A property on your JSON objects to sort on
9364  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9365  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9366  */
9367     sort : function(property, dir, sortType){
9368         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9369         if(this.jsonData){
9370             var p = property;
9371             var dsc = dir && dir.toLowerCase() == "desc";
9372             var f = function(o1, o2){
9373                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9374                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9375                 ;
9376                 if(v1 < v2){
9377                     return dsc ? +1 : -1;
9378                 } else if(v1 > v2){
9379                     return dsc ? -1 : +1;
9380                 } else{
9381                     return 0;
9382                 }
9383             };
9384             this.jsonData.sort(f);
9385             this.refresh();
9386             if(this.jsonData != this.snapshot){
9387                 this.snapshot.sort(f);
9388             }
9389         }
9390     }
9391 });/*
9392  * Based on:
9393  * Ext JS Library 1.1.1
9394  * Copyright(c) 2006-2007, Ext JS, LLC.
9395  *
9396  * Originally Released Under LGPL - original licence link has changed is not relivant.
9397  *
9398  * Fork - LGPL
9399  * <script type="text/javascript">
9400  */
9401  
9402
9403 /**
9404  * @class Roo.ColorPalette
9405  * @extends Roo.Component
9406  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9407  * Here's an example of typical usage:
9408  * <pre><code>
9409 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9410 cp.render('my-div');
9411
9412 cp.on('select', function(palette, selColor){
9413     // do something with selColor
9414 });
9415 </code></pre>
9416  * @constructor
9417  * Create a new ColorPalette
9418  * @param {Object} config The config object
9419  */
9420 Roo.ColorPalette = function(config){
9421     Roo.ColorPalette.superclass.constructor.call(this, config);
9422     this.addEvents({
9423         /**
9424              * @event select
9425              * Fires when a color is selected
9426              * @param {ColorPalette} this
9427              * @param {String} color The 6-digit color hex code (without the # symbol)
9428              */
9429         select: true
9430     });
9431
9432     if(this.handler){
9433         this.on("select", this.handler, this.scope, true);
9434     }
9435 };
9436 Roo.extend(Roo.ColorPalette, Roo.Component, {
9437     /**
9438      * @cfg {String} itemCls
9439      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9440      */
9441     itemCls : "x-color-palette",
9442     /**
9443      * @cfg {String} value
9444      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9445      * the hex codes are case-sensitive.
9446      */
9447     value : null,
9448     clickEvent:'click',
9449     // private
9450     ctype: "Roo.ColorPalette",
9451
9452     /**
9453      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9454      */
9455     allowReselect : false,
9456
9457     /**
9458      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9459      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9460      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9461      * of colors with the width setting until the box is symmetrical.</p>
9462      * <p>You can override individual colors if needed:</p>
9463      * <pre><code>
9464 var cp = new Roo.ColorPalette();
9465 cp.colors[0] = "FF0000";  // change the first box to red
9466 </code></pre>
9467
9468 Or you can provide a custom array of your own for complete control:
9469 <pre><code>
9470 var cp = new Roo.ColorPalette();
9471 cp.colors = ["000000", "993300", "333300"];
9472 </code></pre>
9473      * @type Array
9474      */
9475     colors : [
9476         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9477         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9478         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9479         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9480         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9481     ],
9482
9483     // private
9484     onRender : function(container, position){
9485         var t = new Roo.MasterTemplate(
9486             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9487         );
9488         var c = this.colors;
9489         for(var i = 0, len = c.length; i < len; i++){
9490             t.add([c[i]]);
9491         }
9492         var el = document.createElement("div");
9493         el.className = this.itemCls;
9494         t.overwrite(el);
9495         container.dom.insertBefore(el, position);
9496         this.el = Roo.get(el);
9497         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9498         if(this.clickEvent != 'click'){
9499             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9500         }
9501     },
9502
9503     // private
9504     afterRender : function(){
9505         Roo.ColorPalette.superclass.afterRender.call(this);
9506         if(this.value){
9507             var s = this.value;
9508             this.value = null;
9509             this.select(s);
9510         }
9511     },
9512
9513     // private
9514     handleClick : function(e, t){
9515         e.preventDefault();
9516         if(!this.disabled){
9517             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9518             this.select(c.toUpperCase());
9519         }
9520     },
9521
9522     /**
9523      * Selects the specified color in the palette (fires the select event)
9524      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9525      */
9526     select : function(color){
9527         color = color.replace("#", "");
9528         if(color != this.value || this.allowReselect){
9529             var el = this.el;
9530             if(this.value){
9531                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9532             }
9533             el.child("a.color-"+color).addClass("x-color-palette-sel");
9534             this.value = color;
9535             this.fireEvent("select", this, color);
9536         }
9537     }
9538 });/*
9539  * Based on:
9540  * Ext JS Library 1.1.1
9541  * Copyright(c) 2006-2007, Ext JS, LLC.
9542  *
9543  * Originally Released Under LGPL - original licence link has changed is not relivant.
9544  *
9545  * Fork - LGPL
9546  * <script type="text/javascript">
9547  */
9548  
9549 /**
9550  * @class Roo.DatePicker
9551  * @extends Roo.Component
9552  * Simple date picker class.
9553  * @constructor
9554  * Create a new DatePicker
9555  * @param {Object} config The config object
9556  */
9557 Roo.DatePicker = function(config){
9558     Roo.DatePicker.superclass.constructor.call(this, config);
9559
9560     this.value = config && config.value ?
9561                  config.value.clearTime() : new Date().clearTime();
9562
9563     this.addEvents({
9564         /**
9565              * @event select
9566              * Fires when a date is selected
9567              * @param {DatePicker} this
9568              * @param {Date} date The selected date
9569              */
9570         'select': true,
9571         /**
9572              * @event monthchange
9573              * Fires when the displayed month changes 
9574              * @param {DatePicker} this
9575              * @param {Date} date The selected month
9576              */
9577         'monthchange': true
9578     });
9579
9580     if(this.handler){
9581         this.on("select", this.handler,  this.scope || this);
9582     }
9583     // build the disabledDatesRE
9584     if(!this.disabledDatesRE && this.disabledDates){
9585         var dd = this.disabledDates;
9586         var re = "(?:";
9587         for(var i = 0; i < dd.length; i++){
9588             re += dd[i];
9589             if(i != dd.length-1) re += "|";
9590         }
9591         this.disabledDatesRE = new RegExp(re + ")");
9592     }
9593 };
9594
9595 Roo.extend(Roo.DatePicker, Roo.Component, {
9596     /**
9597      * @cfg {String} todayText
9598      * The text to display on the button that selects the current date (defaults to "Today")
9599      */
9600     todayText : "Today",
9601     /**
9602      * @cfg {String} okText
9603      * The text to display on the ok button
9604      */
9605     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9606     /**
9607      * @cfg {String} cancelText
9608      * The text to display on the cancel button
9609      */
9610     cancelText : "Cancel",
9611     /**
9612      * @cfg {String} todayTip
9613      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9614      */
9615     todayTip : "{0} (Spacebar)",
9616     /**
9617      * @cfg {Date} minDate
9618      * Minimum allowable date (JavaScript date object, defaults to null)
9619      */
9620     minDate : null,
9621     /**
9622      * @cfg {Date} maxDate
9623      * Maximum allowable date (JavaScript date object, defaults to null)
9624      */
9625     maxDate : null,
9626     /**
9627      * @cfg {String} minText
9628      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9629      */
9630     minText : "This date is before the minimum date",
9631     /**
9632      * @cfg {String} maxText
9633      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9634      */
9635     maxText : "This date is after the maximum date",
9636     /**
9637      * @cfg {String} format
9638      * The default date format string which can be overriden for localization support.  The format must be
9639      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9640      */
9641     format : "m/d/y",
9642     /**
9643      * @cfg {Array} disabledDays
9644      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9645      */
9646     disabledDays : null,
9647     /**
9648      * @cfg {String} disabledDaysText
9649      * The tooltip to display when the date falls on a disabled day (defaults to "")
9650      */
9651     disabledDaysText : "",
9652     /**
9653      * @cfg {RegExp} disabledDatesRE
9654      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9655      */
9656     disabledDatesRE : null,
9657     /**
9658      * @cfg {String} disabledDatesText
9659      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9660      */
9661     disabledDatesText : "",
9662     /**
9663      * @cfg {Boolean} constrainToViewport
9664      * True to constrain the date picker to the viewport (defaults to true)
9665      */
9666     constrainToViewport : true,
9667     /**
9668      * @cfg {Array} monthNames
9669      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9670      */
9671     monthNames : Date.monthNames,
9672     /**
9673      * @cfg {Array} dayNames
9674      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9675      */
9676     dayNames : Date.dayNames,
9677     /**
9678      * @cfg {String} nextText
9679      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9680      */
9681     nextText: 'Next Month (Control+Right)',
9682     /**
9683      * @cfg {String} prevText
9684      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9685      */
9686     prevText: 'Previous Month (Control+Left)',
9687     /**
9688      * @cfg {String} monthYearText
9689      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9690      */
9691     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9692     /**
9693      * @cfg {Number} startDay
9694      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9695      */
9696     startDay : 0,
9697     /**
9698      * @cfg {Bool} showClear
9699      * Show a clear button (usefull for date form elements that can be blank.)
9700      */
9701     
9702     showClear: false,
9703     
9704     /**
9705      * Sets the value of the date field
9706      * @param {Date} value The date to set
9707      */
9708     setValue : function(value){
9709         var old = this.value;
9710         
9711         if (typeof(value) == 'string') {
9712          
9713             value = Date.parseDate(value, this.format);
9714         }
9715         if (!value) {
9716             value = new Date();
9717         }
9718         
9719         this.value = value.clearTime(true);
9720         if(this.el){
9721             this.update(this.value);
9722         }
9723     },
9724
9725     /**
9726      * Gets the current selected value of the date field
9727      * @return {Date} The selected date
9728      */
9729     getValue : function(){
9730         return this.value;
9731     },
9732
9733     // private
9734     focus : function(){
9735         if(this.el){
9736             this.update(this.activeDate);
9737         }
9738     },
9739
9740     // privateval
9741     onRender : function(container, position){
9742         
9743         var m = [
9744              '<table cellspacing="0">',
9745                 '<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>',
9746                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9747         var dn = this.dayNames;
9748         for(var i = 0; i < 7; i++){
9749             var d = this.startDay+i;
9750             if(d > 6){
9751                 d = d-7;
9752             }
9753             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9754         }
9755         m[m.length] = "</tr></thead><tbody><tr>";
9756         for(var i = 0; i < 42; i++) {
9757             if(i % 7 == 0 && i != 0){
9758                 m[m.length] = "</tr><tr>";
9759             }
9760             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9761         }
9762         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9763             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9764
9765         var el = document.createElement("div");
9766         el.className = "x-date-picker";
9767         el.innerHTML = m.join("");
9768
9769         container.dom.insertBefore(el, position);
9770
9771         this.el = Roo.get(el);
9772         this.eventEl = Roo.get(el.firstChild);
9773
9774         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9775             handler: this.showPrevMonth,
9776             scope: this,
9777             preventDefault:true,
9778             stopDefault:true
9779         });
9780
9781         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9782             handler: this.showNextMonth,
9783             scope: this,
9784             preventDefault:true,
9785             stopDefault:true
9786         });
9787
9788         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9789
9790         this.monthPicker = this.el.down('div.x-date-mp');
9791         this.monthPicker.enableDisplayMode('block');
9792         
9793         var kn = new Roo.KeyNav(this.eventEl, {
9794             "left" : function(e){
9795                 e.ctrlKey ?
9796                     this.showPrevMonth() :
9797                     this.update(this.activeDate.add("d", -1));
9798             },
9799
9800             "right" : function(e){
9801                 e.ctrlKey ?
9802                     this.showNextMonth() :
9803                     this.update(this.activeDate.add("d", 1));
9804             },
9805
9806             "up" : function(e){
9807                 e.ctrlKey ?
9808                     this.showNextYear() :
9809                     this.update(this.activeDate.add("d", -7));
9810             },
9811
9812             "down" : function(e){
9813                 e.ctrlKey ?
9814                     this.showPrevYear() :
9815                     this.update(this.activeDate.add("d", 7));
9816             },
9817
9818             "pageUp" : function(e){
9819                 this.showNextMonth();
9820             },
9821
9822             "pageDown" : function(e){
9823                 this.showPrevMonth();
9824             },
9825
9826             "enter" : function(e){
9827                 e.stopPropagation();
9828                 return true;
9829             },
9830
9831             scope : this
9832         });
9833
9834         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9835
9836         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9837
9838         this.el.unselectable();
9839         
9840         this.cells = this.el.select("table.x-date-inner tbody td");
9841         this.textNodes = this.el.query("table.x-date-inner tbody span");
9842
9843         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9844             text: "&#160;",
9845             tooltip: this.monthYearText
9846         });
9847
9848         this.mbtn.on('click', this.showMonthPicker, this);
9849         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9850
9851
9852         var today = (new Date()).dateFormat(this.format);
9853         
9854         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9855         if (this.showClear) {
9856             baseTb.add( new Roo.Toolbar.Fill());
9857         }
9858         baseTb.add({
9859             text: String.format(this.todayText, today),
9860             tooltip: String.format(this.todayTip, today),
9861             handler: this.selectToday,
9862             scope: this
9863         });
9864         
9865         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9866             
9867         //});
9868         if (this.showClear) {
9869             
9870             baseTb.add( new Roo.Toolbar.Fill());
9871             baseTb.add({
9872                 text: '&#160;',
9873                 cls: 'x-btn-icon x-btn-clear',
9874                 handler: function() {
9875                     //this.value = '';
9876                     this.fireEvent("select", this, '');
9877                 },
9878                 scope: this
9879             });
9880         }
9881         
9882         
9883         if(Roo.isIE){
9884             this.el.repaint();
9885         }
9886         this.update(this.value);
9887     },
9888
9889     createMonthPicker : function(){
9890         if(!this.monthPicker.dom.firstChild){
9891             var buf = ['<table border="0" cellspacing="0">'];
9892             for(var i = 0; i < 6; i++){
9893                 buf.push(
9894                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9895                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9896                     i == 0 ?
9897                     '<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>' :
9898                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9899                 );
9900             }
9901             buf.push(
9902                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9903                     this.okText,
9904                     '</button><button type="button" class="x-date-mp-cancel">',
9905                     this.cancelText,
9906                     '</button></td></tr>',
9907                 '</table>'
9908             );
9909             this.monthPicker.update(buf.join(''));
9910             this.monthPicker.on('click', this.onMonthClick, this);
9911             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9912
9913             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9914             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9915
9916             this.mpMonths.each(function(m, a, i){
9917                 i += 1;
9918                 if((i%2) == 0){
9919                     m.dom.xmonth = 5 + Math.round(i * .5);
9920                 }else{
9921                     m.dom.xmonth = Math.round((i-1) * .5);
9922                 }
9923             });
9924         }
9925     },
9926
9927     showMonthPicker : function(){
9928         this.createMonthPicker();
9929         var size = this.el.getSize();
9930         this.monthPicker.setSize(size);
9931         this.monthPicker.child('table').setSize(size);
9932
9933         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9934         this.updateMPMonth(this.mpSelMonth);
9935         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9936         this.updateMPYear(this.mpSelYear);
9937
9938         this.monthPicker.slideIn('t', {duration:.2});
9939     },
9940
9941     updateMPYear : function(y){
9942         this.mpyear = y;
9943         var ys = this.mpYears.elements;
9944         for(var i = 1; i <= 10; i++){
9945             var td = ys[i-1], y2;
9946             if((i%2) == 0){
9947                 y2 = y + Math.round(i * .5);
9948                 td.firstChild.innerHTML = y2;
9949                 td.xyear = y2;
9950             }else{
9951                 y2 = y - (5-Math.round(i * .5));
9952                 td.firstChild.innerHTML = y2;
9953                 td.xyear = y2;
9954             }
9955             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9956         }
9957     },
9958
9959     updateMPMonth : function(sm){
9960         this.mpMonths.each(function(m, a, i){
9961             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9962         });
9963     },
9964
9965     selectMPMonth: function(m){
9966         
9967     },
9968
9969     onMonthClick : function(e, t){
9970         e.stopEvent();
9971         var el = new Roo.Element(t), pn;
9972         if(el.is('button.x-date-mp-cancel')){
9973             this.hideMonthPicker();
9974         }
9975         else if(el.is('button.x-date-mp-ok')){
9976             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9977             this.hideMonthPicker();
9978         }
9979         else if(pn = el.up('td.x-date-mp-month', 2)){
9980             this.mpMonths.removeClass('x-date-mp-sel');
9981             pn.addClass('x-date-mp-sel');
9982             this.mpSelMonth = pn.dom.xmonth;
9983         }
9984         else if(pn = el.up('td.x-date-mp-year', 2)){
9985             this.mpYears.removeClass('x-date-mp-sel');
9986             pn.addClass('x-date-mp-sel');
9987             this.mpSelYear = pn.dom.xyear;
9988         }
9989         else if(el.is('a.x-date-mp-prev')){
9990             this.updateMPYear(this.mpyear-10);
9991         }
9992         else if(el.is('a.x-date-mp-next')){
9993             this.updateMPYear(this.mpyear+10);
9994         }
9995     },
9996
9997     onMonthDblClick : function(e, t){
9998         e.stopEvent();
9999         var el = new Roo.Element(t), pn;
10000         if(pn = el.up('td.x-date-mp-month', 2)){
10001             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10002             this.hideMonthPicker();
10003         }
10004         else if(pn = el.up('td.x-date-mp-year', 2)){
10005             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10006             this.hideMonthPicker();
10007         }
10008     },
10009
10010     hideMonthPicker : function(disableAnim){
10011         if(this.monthPicker){
10012             if(disableAnim === true){
10013                 this.monthPicker.hide();
10014             }else{
10015                 this.monthPicker.slideOut('t', {duration:.2});
10016             }
10017         }
10018     },
10019
10020     // private
10021     showPrevMonth : function(e){
10022         this.update(this.activeDate.add("mo", -1));
10023     },
10024
10025     // private
10026     showNextMonth : function(e){
10027         this.update(this.activeDate.add("mo", 1));
10028     },
10029
10030     // private
10031     showPrevYear : function(){
10032         this.update(this.activeDate.add("y", -1));
10033     },
10034
10035     // private
10036     showNextYear : function(){
10037         this.update(this.activeDate.add("y", 1));
10038     },
10039
10040     // private
10041     handleMouseWheel : function(e){
10042         var delta = e.getWheelDelta();
10043         if(delta > 0){
10044             this.showPrevMonth();
10045             e.stopEvent();
10046         } else if(delta < 0){
10047             this.showNextMonth();
10048             e.stopEvent();
10049         }
10050     },
10051
10052     // private
10053     handleDateClick : function(e, t){
10054         e.stopEvent();
10055         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10056             this.setValue(new Date(t.dateValue));
10057             this.fireEvent("select", this, this.value);
10058         }
10059     },
10060
10061     // private
10062     selectToday : function(){
10063         this.setValue(new Date().clearTime());
10064         this.fireEvent("select", this, this.value);
10065     },
10066
10067     // private
10068     update : function(date)
10069     {
10070         var vd = this.activeDate;
10071         this.activeDate = date;
10072         if(vd && this.el){
10073             var t = date.getTime();
10074             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10075                 this.cells.removeClass("x-date-selected");
10076                 this.cells.each(function(c){
10077                    if(c.dom.firstChild.dateValue == t){
10078                        c.addClass("x-date-selected");
10079                        setTimeout(function(){
10080                             try{c.dom.firstChild.focus();}catch(e){}
10081                        }, 50);
10082                        return false;
10083                    }
10084                 });
10085                 return;
10086             }
10087         }
10088         
10089         var days = date.getDaysInMonth();
10090         var firstOfMonth = date.getFirstDateOfMonth();
10091         var startingPos = firstOfMonth.getDay()-this.startDay;
10092
10093         if(startingPos <= this.startDay){
10094             startingPos += 7;
10095         }
10096
10097         var pm = date.add("mo", -1);
10098         var prevStart = pm.getDaysInMonth()-startingPos;
10099
10100         var cells = this.cells.elements;
10101         var textEls = this.textNodes;
10102         days += startingPos;
10103
10104         // convert everything to numbers so it's fast
10105         var day = 86400000;
10106         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10107         var today = new Date().clearTime().getTime();
10108         var sel = date.clearTime().getTime();
10109         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10110         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10111         var ddMatch = this.disabledDatesRE;
10112         var ddText = this.disabledDatesText;
10113         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10114         var ddaysText = this.disabledDaysText;
10115         var format = this.format;
10116
10117         var setCellClass = function(cal, cell){
10118             cell.title = "";
10119             var t = d.getTime();
10120             cell.firstChild.dateValue = t;
10121             if(t == today){
10122                 cell.className += " x-date-today";
10123                 cell.title = cal.todayText;
10124             }
10125             if(t == sel){
10126                 cell.className += " x-date-selected";
10127                 setTimeout(function(){
10128                     try{cell.firstChild.focus();}catch(e){}
10129                 }, 50);
10130             }
10131             // disabling
10132             if(t < min) {
10133                 cell.className = " x-date-disabled";
10134                 cell.title = cal.minText;
10135                 return;
10136             }
10137             if(t > max) {
10138                 cell.className = " x-date-disabled";
10139                 cell.title = cal.maxText;
10140                 return;
10141             }
10142             if(ddays){
10143                 if(ddays.indexOf(d.getDay()) != -1){
10144                     cell.title = ddaysText;
10145                     cell.className = " x-date-disabled";
10146                 }
10147             }
10148             if(ddMatch && format){
10149                 var fvalue = d.dateFormat(format);
10150                 if(ddMatch.test(fvalue)){
10151                     cell.title = ddText.replace("%0", fvalue);
10152                     cell.className = " x-date-disabled";
10153                 }
10154             }
10155         };
10156
10157         var i = 0;
10158         for(; i < startingPos; i++) {
10159             textEls[i].innerHTML = (++prevStart);
10160             d.setDate(d.getDate()+1);
10161             cells[i].className = "x-date-prevday";
10162             setCellClass(this, cells[i]);
10163         }
10164         for(; i < days; i++){
10165             intDay = i - startingPos + 1;
10166             textEls[i].innerHTML = (intDay);
10167             d.setDate(d.getDate()+1);
10168             cells[i].className = "x-date-active";
10169             setCellClass(this, cells[i]);
10170         }
10171         var extraDays = 0;
10172         for(; i < 42; i++) {
10173              textEls[i].innerHTML = (++extraDays);
10174              d.setDate(d.getDate()+1);
10175              cells[i].className = "x-date-nextday";
10176              setCellClass(this, cells[i]);
10177         }
10178
10179         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10180         this.fireEvent('monthchange', this, date);
10181         
10182         if(!this.internalRender){
10183             var main = this.el.dom.firstChild;
10184             var w = main.offsetWidth;
10185             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10186             Roo.fly(main).setWidth(w);
10187             this.internalRender = true;
10188             // opera does not respect the auto grow header center column
10189             // then, after it gets a width opera refuses to recalculate
10190             // without a second pass
10191             if(Roo.isOpera && !this.secondPass){
10192                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10193                 this.secondPass = true;
10194                 this.update.defer(10, this, [date]);
10195             }
10196         }
10197         
10198         
10199     }
10200 });        /*
10201  * Based on:
10202  * Ext JS Library 1.1.1
10203  * Copyright(c) 2006-2007, Ext JS, LLC.
10204  *
10205  * Originally Released Under LGPL - original licence link has changed is not relivant.
10206  *
10207  * Fork - LGPL
10208  * <script type="text/javascript">
10209  */
10210 /**
10211  * @class Roo.TabPanel
10212  * @extends Roo.util.Observable
10213  * A lightweight tab container.
10214  * <br><br>
10215  * Usage:
10216  * <pre><code>
10217 // basic tabs 1, built from existing content
10218 var tabs = new Roo.TabPanel("tabs1");
10219 tabs.addTab("script", "View Script");
10220 tabs.addTab("markup", "View Markup");
10221 tabs.activate("script");
10222
10223 // more advanced tabs, built from javascript
10224 var jtabs = new Roo.TabPanel("jtabs");
10225 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10226
10227 // set up the UpdateManager
10228 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10229 var updater = tab2.getUpdateManager();
10230 updater.setDefaultUrl("ajax1.htm");
10231 tab2.on('activate', updater.refresh, updater, true);
10232
10233 // Use setUrl for Ajax loading
10234 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10235 tab3.setUrl("ajax2.htm", null, true);
10236
10237 // Disabled tab
10238 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10239 tab4.disable();
10240
10241 jtabs.activate("jtabs-1");
10242  * </code></pre>
10243  * @constructor
10244  * Create a new TabPanel.
10245  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10246  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10247  */
10248 Roo.TabPanel = function(container, config){
10249     /**
10250     * The container element for this TabPanel.
10251     * @type Roo.Element
10252     */
10253     this.el = Roo.get(container, true);
10254     if(config){
10255         if(typeof config == "boolean"){
10256             this.tabPosition = config ? "bottom" : "top";
10257         }else{
10258             Roo.apply(this, config);
10259         }
10260     }
10261     if(this.tabPosition == "bottom"){
10262         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10263         this.el.addClass("x-tabs-bottom");
10264     }
10265     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10266     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10267     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10268     if(Roo.isIE){
10269         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10270     }
10271     if(this.tabPosition != "bottom"){
10272         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10273          * @type Roo.Element
10274          */
10275         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10276         this.el.addClass("x-tabs-top");
10277     }
10278     this.items = [];
10279
10280     this.bodyEl.setStyle("position", "relative");
10281
10282     this.active = null;
10283     this.activateDelegate = this.activate.createDelegate(this);
10284
10285     this.addEvents({
10286         /**
10287          * @event tabchange
10288          * Fires when the active tab changes
10289          * @param {Roo.TabPanel} this
10290          * @param {Roo.TabPanelItem} activePanel The new active tab
10291          */
10292         "tabchange": true,
10293         /**
10294          * @event beforetabchange
10295          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10296          * @param {Roo.TabPanel} this
10297          * @param {Object} e Set cancel to true on this object to cancel the tab change
10298          * @param {Roo.TabPanelItem} tab The tab being changed to
10299          */
10300         "beforetabchange" : true
10301     });
10302
10303     Roo.EventManager.onWindowResize(this.onResize, this);
10304     this.cpad = this.el.getPadding("lr");
10305     this.hiddenCount = 0;
10306
10307
10308     // toolbar on the tabbar support...
10309     if (this.toolbar) {
10310         var tcfg = this.toolbar;
10311         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10312         this.toolbar = new Roo.Toolbar(tcfg);
10313         if (Roo.isSafari) {
10314             var tbl = tcfg.container.child('table', true);
10315             tbl.setAttribute('width', '100%');
10316         }
10317         
10318     }
10319    
10320
10321
10322     Roo.TabPanel.superclass.constructor.call(this);
10323 };
10324
10325 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10326     /*
10327      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10328      */
10329     tabPosition : "top",
10330     /*
10331      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10332      */
10333     currentTabWidth : 0,
10334     /*
10335      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10336      */
10337     minTabWidth : 40,
10338     /*
10339      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10340      */
10341     maxTabWidth : 250,
10342     /*
10343      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10344      */
10345     preferredTabWidth : 175,
10346     /*
10347      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10348      */
10349     resizeTabs : false,
10350     /*
10351      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10352      */
10353     monitorResize : true,
10354     /*
10355      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10356      */
10357     toolbar : false,
10358
10359     /**
10360      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10361      * @param {String} id The id of the div to use <b>or create</b>
10362      * @param {String} text The text for the tab
10363      * @param {String} content (optional) Content to put in the TabPanelItem body
10364      * @param {Boolean} closable (optional) True to create a close icon on the tab
10365      * @return {Roo.TabPanelItem} The created TabPanelItem
10366      */
10367     addTab : function(id, text, content, closable){
10368         var item = new Roo.TabPanelItem(this, id, text, closable);
10369         this.addTabItem(item);
10370         if(content){
10371             item.setContent(content);
10372         }
10373         return item;
10374     },
10375
10376     /**
10377      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10378      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10379      * @return {Roo.TabPanelItem}
10380      */
10381     getTab : function(id){
10382         return this.items[id];
10383     },
10384
10385     /**
10386      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10387      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10388      */
10389     hideTab : function(id){
10390         var t = this.items[id];
10391         if(!t.isHidden()){
10392            t.setHidden(true);
10393            this.hiddenCount++;
10394            this.autoSizeTabs();
10395         }
10396     },
10397
10398     /**
10399      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10400      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10401      */
10402     unhideTab : function(id){
10403         var t = this.items[id];
10404         if(t.isHidden()){
10405            t.setHidden(false);
10406            this.hiddenCount--;
10407            this.autoSizeTabs();
10408         }
10409     },
10410
10411     /**
10412      * Adds an existing {@link Roo.TabPanelItem}.
10413      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10414      */
10415     addTabItem : function(item){
10416         this.items[item.id] = item;
10417         this.items.push(item);
10418         if(this.resizeTabs){
10419            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10420            this.autoSizeTabs();
10421         }else{
10422             item.autoSize();
10423         }
10424     },
10425
10426     /**
10427      * Removes a {@link Roo.TabPanelItem}.
10428      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10429      */
10430     removeTab : function(id){
10431         var items = this.items;
10432         var tab = items[id];
10433         if(!tab) { return; }
10434         var index = items.indexOf(tab);
10435         if(this.active == tab && items.length > 1){
10436             var newTab = this.getNextAvailable(index);
10437             if(newTab) {
10438                 newTab.activate();
10439             }
10440         }
10441         this.stripEl.dom.removeChild(tab.pnode.dom);
10442         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10443             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10444         }
10445         items.splice(index, 1);
10446         delete this.items[tab.id];
10447         tab.fireEvent("close", tab);
10448         tab.purgeListeners();
10449         this.autoSizeTabs();
10450     },
10451
10452     getNextAvailable : function(start){
10453         var items = this.items;
10454         var index = start;
10455         // look for a next tab that will slide over to
10456         // replace the one being removed
10457         while(index < items.length){
10458             var item = items[++index];
10459             if(item && !item.isHidden()){
10460                 return item;
10461             }
10462         }
10463         // if one isn't found select the previous tab (on the left)
10464         index = start;
10465         while(index >= 0){
10466             var item = items[--index];
10467             if(item && !item.isHidden()){
10468                 return item;
10469             }
10470         }
10471         return null;
10472     },
10473
10474     /**
10475      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10476      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10477      */
10478     disableTab : function(id){
10479         var tab = this.items[id];
10480         if(tab && this.active != tab){
10481             tab.disable();
10482         }
10483     },
10484
10485     /**
10486      * Enables a {@link Roo.TabPanelItem} that is disabled.
10487      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10488      */
10489     enableTab : function(id){
10490         var tab = this.items[id];
10491         tab.enable();
10492     },
10493
10494     /**
10495      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10496      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10497      * @return {Roo.TabPanelItem} The TabPanelItem.
10498      */
10499     activate : function(id){
10500         var tab = this.items[id];
10501         if(!tab){
10502             return null;
10503         }
10504         if(tab == this.active || tab.disabled){
10505             return tab;
10506         }
10507         var e = {};
10508         this.fireEvent("beforetabchange", this, e, tab);
10509         if(e.cancel !== true && !tab.disabled){
10510             if(this.active){
10511                 this.active.hide();
10512             }
10513             this.active = this.items[id];
10514             this.active.show();
10515             this.fireEvent("tabchange", this, this.active);
10516         }
10517         return tab;
10518     },
10519
10520     /**
10521      * Gets the active {@link Roo.TabPanelItem}.
10522      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10523      */
10524     getActiveTab : function(){
10525         return this.active;
10526     },
10527
10528     /**
10529      * Updates the tab body element to fit the height of the container element
10530      * for overflow scrolling
10531      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10532      */
10533     syncHeight : function(targetHeight){
10534         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10535         var bm = this.bodyEl.getMargins();
10536         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10537         this.bodyEl.setHeight(newHeight);
10538         return newHeight;
10539     },
10540
10541     onResize : function(){
10542         if(this.monitorResize){
10543             this.autoSizeTabs();
10544         }
10545     },
10546
10547     /**
10548      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10549      */
10550     beginUpdate : function(){
10551         this.updating = true;
10552     },
10553
10554     /**
10555      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10556      */
10557     endUpdate : function(){
10558         this.updating = false;
10559         this.autoSizeTabs();
10560     },
10561
10562     /**
10563      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10564      */
10565     autoSizeTabs : function(){
10566         var count = this.items.length;
10567         var vcount = count - this.hiddenCount;
10568         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10569         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10570         var availWidth = Math.floor(w / vcount);
10571         var b = this.stripBody;
10572         if(b.getWidth() > w){
10573             var tabs = this.items;
10574             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10575             if(availWidth < this.minTabWidth){
10576                 /*if(!this.sleft){    // incomplete scrolling code
10577                     this.createScrollButtons();
10578                 }
10579                 this.showScroll();
10580                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10581             }
10582         }else{
10583             if(this.currentTabWidth < this.preferredTabWidth){
10584                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10585             }
10586         }
10587     },
10588
10589     /**
10590      * Returns the number of tabs in this TabPanel.
10591      * @return {Number}
10592      */
10593      getCount : function(){
10594          return this.items.length;
10595      },
10596
10597     /**
10598      * Resizes all the tabs to the passed width
10599      * @param {Number} The new width
10600      */
10601     setTabWidth : function(width){
10602         this.currentTabWidth = width;
10603         for(var i = 0, len = this.items.length; i < len; i++) {
10604                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10605         }
10606     },
10607
10608     /**
10609      * Destroys this TabPanel
10610      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10611      */
10612     destroy : function(removeEl){
10613         Roo.EventManager.removeResizeListener(this.onResize, this);
10614         for(var i = 0, len = this.items.length; i < len; i++){
10615             this.items[i].purgeListeners();
10616         }
10617         if(removeEl === true){
10618             this.el.update("");
10619             this.el.remove();
10620         }
10621     }
10622 });
10623
10624 /**
10625  * @class Roo.TabPanelItem
10626  * @extends Roo.util.Observable
10627  * Represents an individual item (tab plus body) in a TabPanel.
10628  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10629  * @param {String} id The id of this TabPanelItem
10630  * @param {String} text The text for the tab of this TabPanelItem
10631  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10632  */
10633 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10634     /**
10635      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10636      * @type Roo.TabPanel
10637      */
10638     this.tabPanel = tabPanel;
10639     /**
10640      * The id for this TabPanelItem
10641      * @type String
10642      */
10643     this.id = id;
10644     /** @private */
10645     this.disabled = false;
10646     /** @private */
10647     this.text = text;
10648     /** @private */
10649     this.loaded = false;
10650     this.closable = closable;
10651
10652     /**
10653      * The body element for this TabPanelItem.
10654      * @type Roo.Element
10655      */
10656     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10657     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10658     this.bodyEl.setStyle("display", "block");
10659     this.bodyEl.setStyle("zoom", "1");
10660     this.hideAction();
10661
10662     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10663     /** @private */
10664     this.el = Roo.get(els.el, true);
10665     this.inner = Roo.get(els.inner, true);
10666     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10667     this.pnode = Roo.get(els.el.parentNode, true);
10668     this.el.on("mousedown", this.onTabMouseDown, this);
10669     this.el.on("click", this.onTabClick, this);
10670     /** @private */
10671     if(closable){
10672         var c = Roo.get(els.close, true);
10673         c.dom.title = this.closeText;
10674         c.addClassOnOver("close-over");
10675         c.on("click", this.closeClick, this);
10676      }
10677
10678     this.addEvents({
10679          /**
10680          * @event activate
10681          * Fires when this tab becomes the active tab.
10682          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10683          * @param {Roo.TabPanelItem} this
10684          */
10685         "activate": true,
10686         /**
10687          * @event beforeclose
10688          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10689          * @param {Roo.TabPanelItem} this
10690          * @param {Object} e Set cancel to true on this object to cancel the close.
10691          */
10692         "beforeclose": true,
10693         /**
10694          * @event close
10695          * Fires when this tab is closed.
10696          * @param {Roo.TabPanelItem} this
10697          */
10698          "close": true,
10699         /**
10700          * @event deactivate
10701          * Fires when this tab is no longer the active tab.
10702          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10703          * @param {Roo.TabPanelItem} this
10704          */
10705          "deactivate" : true
10706     });
10707     this.hidden = false;
10708
10709     Roo.TabPanelItem.superclass.constructor.call(this);
10710 };
10711
10712 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10713     purgeListeners : function(){
10714        Roo.util.Observable.prototype.purgeListeners.call(this);
10715        this.el.removeAllListeners();
10716     },
10717     /**
10718      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10719      */
10720     show : function(){
10721         this.pnode.addClass("on");
10722         this.showAction();
10723         if(Roo.isOpera){
10724             this.tabPanel.stripWrap.repaint();
10725         }
10726         this.fireEvent("activate", this.tabPanel, this);
10727     },
10728
10729     /**
10730      * Returns true if this tab is the active tab.
10731      * @return {Boolean}
10732      */
10733     isActive : function(){
10734         return this.tabPanel.getActiveTab() == this;
10735     },
10736
10737     /**
10738      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10739      */
10740     hide : function(){
10741         this.pnode.removeClass("on");
10742         this.hideAction();
10743         this.fireEvent("deactivate", this.tabPanel, this);
10744     },
10745
10746     hideAction : function(){
10747         this.bodyEl.hide();
10748         this.bodyEl.setStyle("position", "absolute");
10749         this.bodyEl.setLeft("-20000px");
10750         this.bodyEl.setTop("-20000px");
10751     },
10752
10753     showAction : function(){
10754         this.bodyEl.setStyle("position", "relative");
10755         this.bodyEl.setTop("");
10756         this.bodyEl.setLeft("");
10757         this.bodyEl.show();
10758     },
10759
10760     /**
10761      * Set the tooltip for the tab.
10762      * @param {String} tooltip The tab's tooltip
10763      */
10764     setTooltip : function(text){
10765         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10766             this.textEl.dom.qtip = text;
10767             this.textEl.dom.removeAttribute('title');
10768         }else{
10769             this.textEl.dom.title = text;
10770         }
10771     },
10772
10773     onTabClick : function(e){
10774         e.preventDefault();
10775         this.tabPanel.activate(this.id);
10776     },
10777
10778     onTabMouseDown : function(e){
10779         e.preventDefault();
10780         this.tabPanel.activate(this.id);
10781     },
10782
10783     getWidth : function(){
10784         return this.inner.getWidth();
10785     },
10786
10787     setWidth : function(width){
10788         var iwidth = width - this.pnode.getPadding("lr");
10789         this.inner.setWidth(iwidth);
10790         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10791         this.pnode.setWidth(width);
10792     },
10793
10794     /**
10795      * Show or hide the tab
10796      * @param {Boolean} hidden True to hide or false to show.
10797      */
10798     setHidden : function(hidden){
10799         this.hidden = hidden;
10800         this.pnode.setStyle("display", hidden ? "none" : "");
10801     },
10802
10803     /**
10804      * Returns true if this tab is "hidden"
10805      * @return {Boolean}
10806      */
10807     isHidden : function(){
10808         return this.hidden;
10809     },
10810
10811     /**
10812      * Returns the text for this tab
10813      * @return {String}
10814      */
10815     getText : function(){
10816         return this.text;
10817     },
10818
10819     autoSize : function(){
10820         //this.el.beginMeasure();
10821         this.textEl.setWidth(1);
10822         /*
10823          *  #2804 [new] Tabs in Roojs
10824          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10825          */
10826         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10827         //this.el.endMeasure();
10828     },
10829
10830     /**
10831      * Sets the text for the tab (Note: this also sets the tooltip text)
10832      * @param {String} text The tab's text and tooltip
10833      */
10834     setText : function(text){
10835         this.text = text;
10836         this.textEl.update(text);
10837         this.setTooltip(text);
10838         if(!this.tabPanel.resizeTabs){
10839             this.autoSize();
10840         }
10841     },
10842     /**
10843      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10844      */
10845     activate : function(){
10846         this.tabPanel.activate(this.id);
10847     },
10848
10849     /**
10850      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10851      */
10852     disable : function(){
10853         if(this.tabPanel.active != this){
10854             this.disabled = true;
10855             this.pnode.addClass("disabled");
10856         }
10857     },
10858
10859     /**
10860      * Enables this TabPanelItem if it was previously disabled.
10861      */
10862     enable : function(){
10863         this.disabled = false;
10864         this.pnode.removeClass("disabled");
10865     },
10866
10867     /**
10868      * Sets the content for this TabPanelItem.
10869      * @param {String} content The content
10870      * @param {Boolean} loadScripts true to look for and load scripts
10871      */
10872     setContent : function(content, loadScripts){
10873         this.bodyEl.update(content, loadScripts);
10874     },
10875
10876     /**
10877      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10878      * @return {Roo.UpdateManager} The UpdateManager
10879      */
10880     getUpdateManager : function(){
10881         return this.bodyEl.getUpdateManager();
10882     },
10883
10884     /**
10885      * Set a URL to be used to load the content for this TabPanelItem.
10886      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10887      * @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)
10888      * @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)
10889      * @return {Roo.UpdateManager} The UpdateManager
10890      */
10891     setUrl : function(url, params, loadOnce){
10892         if(this.refreshDelegate){
10893             this.un('activate', this.refreshDelegate);
10894         }
10895         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10896         this.on("activate", this.refreshDelegate);
10897         return this.bodyEl.getUpdateManager();
10898     },
10899
10900     /** @private */
10901     _handleRefresh : function(url, params, loadOnce){
10902         if(!loadOnce || !this.loaded){
10903             var updater = this.bodyEl.getUpdateManager();
10904             updater.update(url, params, this._setLoaded.createDelegate(this));
10905         }
10906     },
10907
10908     /**
10909      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10910      *   Will fail silently if the setUrl method has not been called.
10911      *   This does not activate the panel, just updates its content.
10912      */
10913     refresh : function(){
10914         if(this.refreshDelegate){
10915            this.loaded = false;
10916            this.refreshDelegate();
10917         }
10918     },
10919
10920     /** @private */
10921     _setLoaded : function(){
10922         this.loaded = true;
10923     },
10924
10925     /** @private */
10926     closeClick : function(e){
10927         var o = {};
10928         e.stopEvent();
10929         this.fireEvent("beforeclose", this, o);
10930         if(o.cancel !== true){
10931             this.tabPanel.removeTab(this.id);
10932         }
10933     },
10934     /**
10935      * The text displayed in the tooltip for the close icon.
10936      * @type String
10937      */
10938     closeText : "Close this tab"
10939 });
10940
10941 /** @private */
10942 Roo.TabPanel.prototype.createStrip = function(container){
10943     var strip = document.createElement("div");
10944     strip.className = "x-tabs-wrap";
10945     container.appendChild(strip);
10946     return strip;
10947 };
10948 /** @private */
10949 Roo.TabPanel.prototype.createStripList = function(strip){
10950     // div wrapper for retard IE
10951     // returns the "tr" element.
10952     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10953         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10954         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10955     return strip.firstChild.firstChild.firstChild.firstChild;
10956 };
10957 /** @private */
10958 Roo.TabPanel.prototype.createBody = function(container){
10959     var body = document.createElement("div");
10960     Roo.id(body, "tab-body");
10961     Roo.fly(body).addClass("x-tabs-body");
10962     container.appendChild(body);
10963     return body;
10964 };
10965 /** @private */
10966 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10967     var body = Roo.getDom(id);
10968     if(!body){
10969         body = document.createElement("div");
10970         body.id = id;
10971     }
10972     Roo.fly(body).addClass("x-tabs-item-body");
10973     bodyEl.insertBefore(body, bodyEl.firstChild);
10974     return body;
10975 };
10976 /** @private */
10977 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10978     var td = document.createElement("td");
10979     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10980     //stripEl.appendChild(td);
10981     if(closable){
10982         td.className = "x-tabs-closable";
10983         if(!this.closeTpl){
10984             this.closeTpl = new Roo.Template(
10985                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10986                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10987                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10988             );
10989         }
10990         var el = this.closeTpl.overwrite(td, {"text": text});
10991         var close = el.getElementsByTagName("div")[0];
10992         var inner = el.getElementsByTagName("em")[0];
10993         return {"el": el, "close": close, "inner": inner};
10994     } else {
10995         if(!this.tabTpl){
10996             this.tabTpl = new Roo.Template(
10997                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10998                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10999             );
11000         }
11001         var el = this.tabTpl.overwrite(td, {"text": text});
11002         var inner = el.getElementsByTagName("em")[0];
11003         return {"el": el, "inner": inner};
11004     }
11005 };/*
11006  * Based on:
11007  * Ext JS Library 1.1.1
11008  * Copyright(c) 2006-2007, Ext JS, LLC.
11009  *
11010  * Originally Released Under LGPL - original licence link has changed is not relivant.
11011  *
11012  * Fork - LGPL
11013  * <script type="text/javascript">
11014  */
11015
11016 /**
11017  * @class Roo.Button
11018  * @extends Roo.util.Observable
11019  * Simple Button class
11020  * @cfg {String} text The button text
11021  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11022  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11023  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11024  * @cfg {Object} scope The scope of the handler
11025  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11026  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11027  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11028  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11029  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11030  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11031    applies if enableToggle = true)
11032  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11033  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11034   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11035  * @constructor
11036  * Create a new button
11037  * @param {Object} config The config object
11038  */
11039 Roo.Button = function(renderTo, config)
11040 {
11041     if (!config) {
11042         config = renderTo;
11043         renderTo = config.renderTo || false;
11044     }
11045     
11046     Roo.apply(this, config);
11047     this.addEvents({
11048         /**
11049              * @event click
11050              * Fires when this button is clicked
11051              * @param {Button} this
11052              * @param {EventObject} e The click event
11053              */
11054             "click" : true,
11055         /**
11056              * @event toggle
11057              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11058              * @param {Button} this
11059              * @param {Boolean} pressed
11060              */
11061             "toggle" : true,
11062         /**
11063              * @event mouseover
11064              * Fires when the mouse hovers over the button
11065              * @param {Button} this
11066              * @param {Event} e The event object
11067              */
11068         'mouseover' : true,
11069         /**
11070              * @event mouseout
11071              * Fires when the mouse exits the button
11072              * @param {Button} this
11073              * @param {Event} e The event object
11074              */
11075         'mouseout': true,
11076          /**
11077              * @event render
11078              * Fires when the button is rendered
11079              * @param {Button} this
11080              */
11081         'render': true
11082     });
11083     if(this.menu){
11084         this.menu = Roo.menu.MenuMgr.get(this.menu);
11085     }
11086     // register listeners first!!  - so render can be captured..
11087     Roo.util.Observable.call(this);
11088     if(renderTo){
11089         this.render(renderTo);
11090     }
11091     
11092   
11093 };
11094
11095 Roo.extend(Roo.Button, Roo.util.Observable, {
11096     /**
11097      * 
11098      */
11099     
11100     /**
11101      * Read-only. True if this button is hidden
11102      * @type Boolean
11103      */
11104     hidden : false,
11105     /**
11106      * Read-only. True if this button is disabled
11107      * @type Boolean
11108      */
11109     disabled : false,
11110     /**
11111      * Read-only. True if this button is pressed (only if enableToggle = true)
11112      * @type Boolean
11113      */
11114     pressed : false,
11115
11116     /**
11117      * @cfg {Number} tabIndex 
11118      * The DOM tabIndex for this button (defaults to undefined)
11119      */
11120     tabIndex : undefined,
11121
11122     /**
11123      * @cfg {Boolean} enableToggle
11124      * True to enable pressed/not pressed toggling (defaults to false)
11125      */
11126     enableToggle: false,
11127     /**
11128      * @cfg {Mixed} menu
11129      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11130      */
11131     menu : undefined,
11132     /**
11133      * @cfg {String} menuAlign
11134      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11135      */
11136     menuAlign : "tl-bl?",
11137
11138     /**
11139      * @cfg {String} iconCls
11140      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11141      */
11142     iconCls : undefined,
11143     /**
11144      * @cfg {String} type
11145      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11146      */
11147     type : 'button',
11148
11149     // private
11150     menuClassTarget: 'tr',
11151
11152     /**
11153      * @cfg {String} clickEvent
11154      * The type of event to map to the button's event handler (defaults to 'click')
11155      */
11156     clickEvent : 'click',
11157
11158     /**
11159      * @cfg {Boolean} handleMouseEvents
11160      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11161      */
11162     handleMouseEvents : true,
11163
11164     /**
11165      * @cfg {String} tooltipType
11166      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11167      */
11168     tooltipType : 'qtip',
11169
11170     /**
11171      * @cfg {String} cls
11172      * A CSS class to apply to the button's main element.
11173      */
11174     
11175     /**
11176      * @cfg {Roo.Template} template (Optional)
11177      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11178      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11179      * require code modifications if required elements (e.g. a button) aren't present.
11180      */
11181
11182     // private
11183     render : function(renderTo){
11184         var btn;
11185         if(this.hideParent){
11186             this.parentEl = Roo.get(renderTo);
11187         }
11188         if(!this.dhconfig){
11189             if(!this.template){
11190                 if(!Roo.Button.buttonTemplate){
11191                     // hideous table template
11192                     Roo.Button.buttonTemplate = new Roo.Template(
11193                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11194                         '<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>',
11195                         "</tr></tbody></table>");
11196                 }
11197                 this.template = Roo.Button.buttonTemplate;
11198             }
11199             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11200             var btnEl = btn.child("button:first");
11201             btnEl.on('focus', this.onFocus, this);
11202             btnEl.on('blur', this.onBlur, this);
11203             if(this.cls){
11204                 btn.addClass(this.cls);
11205             }
11206             if(this.icon){
11207                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11208             }
11209             if(this.iconCls){
11210                 btnEl.addClass(this.iconCls);
11211                 if(!this.cls){
11212                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11213                 }
11214             }
11215             if(this.tabIndex !== undefined){
11216                 btnEl.dom.tabIndex = this.tabIndex;
11217             }
11218             if(this.tooltip){
11219                 if(typeof this.tooltip == 'object'){
11220                     Roo.QuickTips.tips(Roo.apply({
11221                           target: btnEl.id
11222                     }, this.tooltip));
11223                 } else {
11224                     btnEl.dom[this.tooltipType] = this.tooltip;
11225                 }
11226             }
11227         }else{
11228             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11229         }
11230         this.el = btn;
11231         if(this.id){
11232             this.el.dom.id = this.el.id = this.id;
11233         }
11234         if(this.menu){
11235             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11236             this.menu.on("show", this.onMenuShow, this);
11237             this.menu.on("hide", this.onMenuHide, this);
11238         }
11239         btn.addClass("x-btn");
11240         if(Roo.isIE && !Roo.isIE7){
11241             this.autoWidth.defer(1, this);
11242         }else{
11243             this.autoWidth();
11244         }
11245         if(this.handleMouseEvents){
11246             btn.on("mouseover", this.onMouseOver, this);
11247             btn.on("mouseout", this.onMouseOut, this);
11248             btn.on("mousedown", this.onMouseDown, this);
11249         }
11250         btn.on(this.clickEvent, this.onClick, this);
11251         //btn.on("mouseup", this.onMouseUp, this);
11252         if(this.hidden){
11253             this.hide();
11254         }
11255         if(this.disabled){
11256             this.disable();
11257         }
11258         Roo.ButtonToggleMgr.register(this);
11259         if(this.pressed){
11260             this.el.addClass("x-btn-pressed");
11261         }
11262         if(this.repeat){
11263             var repeater = new Roo.util.ClickRepeater(btn,
11264                 typeof this.repeat == "object" ? this.repeat : {}
11265             );
11266             repeater.on("click", this.onClick,  this);
11267         }
11268         
11269         this.fireEvent('render', this);
11270         
11271     },
11272     /**
11273      * Returns the button's underlying element
11274      * @return {Roo.Element} The element
11275      */
11276     getEl : function(){
11277         return this.el;  
11278     },
11279     
11280     /**
11281      * Destroys this Button and removes any listeners.
11282      */
11283     destroy : function(){
11284         Roo.ButtonToggleMgr.unregister(this);
11285         this.el.removeAllListeners();
11286         this.purgeListeners();
11287         this.el.remove();
11288     },
11289
11290     // private
11291     autoWidth : function(){
11292         if(this.el){
11293             this.el.setWidth("auto");
11294             if(Roo.isIE7 && Roo.isStrict){
11295                 var ib = this.el.child('button');
11296                 if(ib && ib.getWidth() > 20){
11297                     ib.clip();
11298                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11299                 }
11300             }
11301             if(this.minWidth){
11302                 if(this.hidden){
11303                     this.el.beginMeasure();
11304                 }
11305                 if(this.el.getWidth() < this.minWidth){
11306                     this.el.setWidth(this.minWidth);
11307                 }
11308                 if(this.hidden){
11309                     this.el.endMeasure();
11310                 }
11311             }
11312         }
11313     },
11314
11315     /**
11316      * Assigns this button's click handler
11317      * @param {Function} handler The function to call when the button is clicked
11318      * @param {Object} scope (optional) Scope for the function passed in
11319      */
11320     setHandler : function(handler, scope){
11321         this.handler = handler;
11322         this.scope = scope;  
11323     },
11324     
11325     /**
11326      * Sets this button's text
11327      * @param {String} text The button text
11328      */
11329     setText : function(text){
11330         this.text = text;
11331         if(this.el){
11332             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11333         }
11334         this.autoWidth();
11335     },
11336     
11337     /**
11338      * Gets the text for this button
11339      * @return {String} The button text
11340      */
11341     getText : function(){
11342         return this.text;  
11343     },
11344     
11345     /**
11346      * Show this button
11347      */
11348     show: function(){
11349         this.hidden = false;
11350         if(this.el){
11351             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11352         }
11353     },
11354     
11355     /**
11356      * Hide this button
11357      */
11358     hide: function(){
11359         this.hidden = true;
11360         if(this.el){
11361             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11362         }
11363     },
11364     
11365     /**
11366      * Convenience function for boolean show/hide
11367      * @param {Boolean} visible True to show, false to hide
11368      */
11369     setVisible: function(visible){
11370         if(visible) {
11371             this.show();
11372         }else{
11373             this.hide();
11374         }
11375     },
11376     
11377     /**
11378      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11379      * @param {Boolean} state (optional) Force a particular state
11380      */
11381     toggle : function(state){
11382         state = state === undefined ? !this.pressed : state;
11383         if(state != this.pressed){
11384             if(state){
11385                 this.el.addClass("x-btn-pressed");
11386                 this.pressed = true;
11387                 this.fireEvent("toggle", this, true);
11388             }else{
11389                 this.el.removeClass("x-btn-pressed");
11390                 this.pressed = false;
11391                 this.fireEvent("toggle", this, false);
11392             }
11393             if(this.toggleHandler){
11394                 this.toggleHandler.call(this.scope || this, this, state);
11395             }
11396         }
11397     },
11398     
11399     /**
11400      * Focus the button
11401      */
11402     focus : function(){
11403         this.el.child('button:first').focus();
11404     },
11405     
11406     /**
11407      * Disable this button
11408      */
11409     disable : function(){
11410         if(this.el){
11411             this.el.addClass("x-btn-disabled");
11412         }
11413         this.disabled = true;
11414     },
11415     
11416     /**
11417      * Enable this button
11418      */
11419     enable : function(){
11420         if(this.el){
11421             this.el.removeClass("x-btn-disabled");
11422         }
11423         this.disabled = false;
11424     },
11425
11426     /**
11427      * Convenience function for boolean enable/disable
11428      * @param {Boolean} enabled True to enable, false to disable
11429      */
11430     setDisabled : function(v){
11431         this[v !== true ? "enable" : "disable"]();
11432     },
11433
11434     // private
11435     onClick : function(e)
11436     {
11437         if(e){
11438             e.preventDefault();
11439         }
11440         if(e.button != 0){
11441             return;
11442         }
11443         if(!this.disabled){
11444             if(this.enableToggle){
11445                 this.toggle();
11446             }
11447             if(this.menu && !this.menu.isVisible()){
11448                 this.menu.show(this.el, this.menuAlign);
11449             }
11450             this.fireEvent("click", this, e);
11451             if(this.handler){
11452                 this.el.removeClass("x-btn-over");
11453                 this.handler.call(this.scope || this, this, e);
11454             }
11455         }
11456     },
11457     // private
11458     onMouseOver : function(e){
11459         if(!this.disabled){
11460             this.el.addClass("x-btn-over");
11461             this.fireEvent('mouseover', this, e);
11462         }
11463     },
11464     // private
11465     onMouseOut : function(e){
11466         if(!e.within(this.el,  true)){
11467             this.el.removeClass("x-btn-over");
11468             this.fireEvent('mouseout', this, e);
11469         }
11470     },
11471     // private
11472     onFocus : function(e){
11473         if(!this.disabled){
11474             this.el.addClass("x-btn-focus");
11475         }
11476     },
11477     // private
11478     onBlur : function(e){
11479         this.el.removeClass("x-btn-focus");
11480     },
11481     // private
11482     onMouseDown : function(e){
11483         if(!this.disabled && e.button == 0){
11484             this.el.addClass("x-btn-click");
11485             Roo.get(document).on('mouseup', this.onMouseUp, this);
11486         }
11487     },
11488     // private
11489     onMouseUp : function(e){
11490         if(e.button == 0){
11491             this.el.removeClass("x-btn-click");
11492             Roo.get(document).un('mouseup', this.onMouseUp, this);
11493         }
11494     },
11495     // private
11496     onMenuShow : function(e){
11497         this.el.addClass("x-btn-menu-active");
11498     },
11499     // private
11500     onMenuHide : function(e){
11501         this.el.removeClass("x-btn-menu-active");
11502     }   
11503 });
11504
11505 // Private utility class used by Button
11506 Roo.ButtonToggleMgr = function(){
11507    var groups = {};
11508    
11509    function toggleGroup(btn, state){
11510        if(state){
11511            var g = groups[btn.toggleGroup];
11512            for(var i = 0, l = g.length; i < l; i++){
11513                if(g[i] != btn){
11514                    g[i].toggle(false);
11515                }
11516            }
11517        }
11518    }
11519    
11520    return {
11521        register : function(btn){
11522            if(!btn.toggleGroup){
11523                return;
11524            }
11525            var g = groups[btn.toggleGroup];
11526            if(!g){
11527                g = groups[btn.toggleGroup] = [];
11528            }
11529            g.push(btn);
11530            btn.on("toggle", toggleGroup);
11531        },
11532        
11533        unregister : function(btn){
11534            if(!btn.toggleGroup){
11535                return;
11536            }
11537            var g = groups[btn.toggleGroup];
11538            if(g){
11539                g.remove(btn);
11540                btn.un("toggle", toggleGroup);
11541            }
11542        }
11543    };
11544 }();/*
11545  * Based on:
11546  * Ext JS Library 1.1.1
11547  * Copyright(c) 2006-2007, Ext JS, LLC.
11548  *
11549  * Originally Released Under LGPL - original licence link has changed is not relivant.
11550  *
11551  * Fork - LGPL
11552  * <script type="text/javascript">
11553  */
11554  
11555 /**
11556  * @class Roo.SplitButton
11557  * @extends Roo.Button
11558  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11559  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11560  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11561  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11562  * @cfg {String} arrowTooltip The title attribute of the arrow
11563  * @constructor
11564  * Create a new menu button
11565  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11566  * @param {Object} config The config object
11567  */
11568 Roo.SplitButton = function(renderTo, config){
11569     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11570     /**
11571      * @event arrowclick
11572      * Fires when this button's arrow is clicked
11573      * @param {SplitButton} this
11574      * @param {EventObject} e The click event
11575      */
11576     this.addEvents({"arrowclick":true});
11577 };
11578
11579 Roo.extend(Roo.SplitButton, Roo.Button, {
11580     render : function(renderTo){
11581         // this is one sweet looking template!
11582         var tpl = new Roo.Template(
11583             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11584             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11585             '<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>',
11586             "</tbody></table></td><td>",
11587             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11588             '<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>',
11589             "</tbody></table></td></tr></table>"
11590         );
11591         var btn = tpl.append(renderTo, [this.text, this.type], true);
11592         var btnEl = btn.child("button");
11593         if(this.cls){
11594             btn.addClass(this.cls);
11595         }
11596         if(this.icon){
11597             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11598         }
11599         if(this.iconCls){
11600             btnEl.addClass(this.iconCls);
11601             if(!this.cls){
11602                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11603             }
11604         }
11605         this.el = btn;
11606         if(this.handleMouseEvents){
11607             btn.on("mouseover", this.onMouseOver, this);
11608             btn.on("mouseout", this.onMouseOut, this);
11609             btn.on("mousedown", this.onMouseDown, this);
11610             btn.on("mouseup", this.onMouseUp, this);
11611         }
11612         btn.on(this.clickEvent, this.onClick, this);
11613         if(this.tooltip){
11614             if(typeof this.tooltip == 'object'){
11615                 Roo.QuickTips.tips(Roo.apply({
11616                       target: btnEl.id
11617                 }, this.tooltip));
11618             } else {
11619                 btnEl.dom[this.tooltipType] = this.tooltip;
11620             }
11621         }
11622         if(this.arrowTooltip){
11623             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11624         }
11625         if(this.hidden){
11626             this.hide();
11627         }
11628         if(this.disabled){
11629             this.disable();
11630         }
11631         if(this.pressed){
11632             this.el.addClass("x-btn-pressed");
11633         }
11634         if(Roo.isIE && !Roo.isIE7){
11635             this.autoWidth.defer(1, this);
11636         }else{
11637             this.autoWidth();
11638         }
11639         if(this.menu){
11640             this.menu.on("show", this.onMenuShow, this);
11641             this.menu.on("hide", this.onMenuHide, this);
11642         }
11643         this.fireEvent('render', this);
11644     },
11645
11646     // private
11647     autoWidth : function(){
11648         if(this.el){
11649             var tbl = this.el.child("table:first");
11650             var tbl2 = this.el.child("table:last");
11651             this.el.setWidth("auto");
11652             tbl.setWidth("auto");
11653             if(Roo.isIE7 && Roo.isStrict){
11654                 var ib = this.el.child('button:first');
11655                 if(ib && ib.getWidth() > 20){
11656                     ib.clip();
11657                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11658                 }
11659             }
11660             if(this.minWidth){
11661                 if(this.hidden){
11662                     this.el.beginMeasure();
11663                 }
11664                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11665                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11666                 }
11667                 if(this.hidden){
11668                     this.el.endMeasure();
11669                 }
11670             }
11671             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11672         } 
11673     },
11674     /**
11675      * Sets this button's click handler
11676      * @param {Function} handler The function to call when the button is clicked
11677      * @param {Object} scope (optional) Scope for the function passed above
11678      */
11679     setHandler : function(handler, scope){
11680         this.handler = handler;
11681         this.scope = scope;  
11682     },
11683     
11684     /**
11685      * Sets this button's arrow click handler
11686      * @param {Function} handler The function to call when the arrow is clicked
11687      * @param {Object} scope (optional) Scope for the function passed above
11688      */
11689     setArrowHandler : function(handler, scope){
11690         this.arrowHandler = handler;
11691         this.scope = scope;  
11692     },
11693     
11694     /**
11695      * Focus the button
11696      */
11697     focus : function(){
11698         if(this.el){
11699             this.el.child("button:first").focus();
11700         }
11701     },
11702
11703     // private
11704     onClick : function(e){
11705         e.preventDefault();
11706         if(!this.disabled){
11707             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11708                 if(this.menu && !this.menu.isVisible()){
11709                     this.menu.show(this.el, this.menuAlign);
11710                 }
11711                 this.fireEvent("arrowclick", this, e);
11712                 if(this.arrowHandler){
11713                     this.arrowHandler.call(this.scope || this, this, e);
11714                 }
11715             }else{
11716                 this.fireEvent("click", this, e);
11717                 if(this.handler){
11718                     this.handler.call(this.scope || this, this, e);
11719                 }
11720             }
11721         }
11722     },
11723     // private
11724     onMouseDown : function(e){
11725         if(!this.disabled){
11726             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11727         }
11728     },
11729     // private
11730     onMouseUp : function(e){
11731         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11732     }   
11733 });
11734
11735
11736 // backwards compat
11737 Roo.MenuButton = Roo.SplitButton;/*
11738  * Based on:
11739  * Ext JS Library 1.1.1
11740  * Copyright(c) 2006-2007, Ext JS, LLC.
11741  *
11742  * Originally Released Under LGPL - original licence link has changed is not relivant.
11743  *
11744  * Fork - LGPL
11745  * <script type="text/javascript">
11746  */
11747
11748 /**
11749  * @class Roo.Toolbar
11750  * Basic Toolbar class.
11751  * @constructor
11752  * Creates a new Toolbar
11753  * @param {Object} container The config object
11754  */ 
11755 Roo.Toolbar = function(container, buttons, config)
11756 {
11757     /// old consturctor format still supported..
11758     if(container instanceof Array){ // omit the container for later rendering
11759         buttons = container;
11760         config = buttons;
11761         container = null;
11762     }
11763     if (typeof(container) == 'object' && container.xtype) {
11764         config = container;
11765         container = config.container;
11766         buttons = config.buttons || []; // not really - use items!!
11767     }
11768     var xitems = [];
11769     if (config && config.items) {
11770         xitems = config.items;
11771         delete config.items;
11772     }
11773     Roo.apply(this, config);
11774     this.buttons = buttons;
11775     
11776     if(container){
11777         this.render(container);
11778     }
11779     this.xitems = xitems;
11780     Roo.each(xitems, function(b) {
11781         this.add(b);
11782     }, this);
11783     
11784 };
11785
11786 Roo.Toolbar.prototype = {
11787     /**
11788      * @cfg {Array} items
11789      * array of button configs or elements to add (will be converted to a MixedCollection)
11790      */
11791     
11792     /**
11793      * @cfg {String/HTMLElement/Element} container
11794      * The id or element that will contain the toolbar
11795      */
11796     // private
11797     render : function(ct){
11798         this.el = Roo.get(ct);
11799         if(this.cls){
11800             this.el.addClass(this.cls);
11801         }
11802         // using a table allows for vertical alignment
11803         // 100% width is needed by Safari...
11804         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11805         this.tr = this.el.child("tr", true);
11806         var autoId = 0;
11807         this.items = new Roo.util.MixedCollection(false, function(o){
11808             return o.id || ("item" + (++autoId));
11809         });
11810         if(this.buttons){
11811             this.add.apply(this, this.buttons);
11812             delete this.buttons;
11813         }
11814     },
11815
11816     /**
11817      * Adds element(s) to the toolbar -- this function takes a variable number of 
11818      * arguments of mixed type and adds them to the toolbar.
11819      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11820      * <ul>
11821      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11822      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11823      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11824      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11825      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11826      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11827      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11828      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11829      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11830      * </ul>
11831      * @param {Mixed} arg2
11832      * @param {Mixed} etc.
11833      */
11834     add : function(){
11835         var a = arguments, l = a.length;
11836         for(var i = 0; i < l; i++){
11837             this._add(a[i]);
11838         }
11839     },
11840     // private..
11841     _add : function(el) {
11842         
11843         if (el.xtype) {
11844             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11845         }
11846         
11847         if (el.applyTo){ // some kind of form field
11848             return this.addField(el);
11849         } 
11850         if (el.render){ // some kind of Toolbar.Item
11851             return this.addItem(el);
11852         }
11853         if (typeof el == "string"){ // string
11854             if(el == "separator" || el == "-"){
11855                 return this.addSeparator();
11856             }
11857             if (el == " "){
11858                 return this.addSpacer();
11859             }
11860             if(el == "->"){
11861                 return this.addFill();
11862             }
11863             return this.addText(el);
11864             
11865         }
11866         if(el.tagName){ // element
11867             return this.addElement(el);
11868         }
11869         if(typeof el == "object"){ // must be button config?
11870             return this.addButton(el);
11871         }
11872         // and now what?!?!
11873         return false;
11874         
11875     },
11876     
11877     /**
11878      * Add an Xtype element
11879      * @param {Object} xtype Xtype Object
11880      * @return {Object} created Object
11881      */
11882     addxtype : function(e){
11883         return this.add(e);  
11884     },
11885     
11886     /**
11887      * Returns the Element for this toolbar.
11888      * @return {Roo.Element}
11889      */
11890     getEl : function(){
11891         return this.el;  
11892     },
11893     
11894     /**
11895      * Adds a separator
11896      * @return {Roo.Toolbar.Item} The separator item
11897      */
11898     addSeparator : function(){
11899         return this.addItem(new Roo.Toolbar.Separator());
11900     },
11901
11902     /**
11903      * Adds a spacer element
11904      * @return {Roo.Toolbar.Spacer} The spacer item
11905      */
11906     addSpacer : function(){
11907         return this.addItem(new Roo.Toolbar.Spacer());
11908     },
11909
11910     /**
11911      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11912      * @return {Roo.Toolbar.Fill} The fill item
11913      */
11914     addFill : function(){
11915         return this.addItem(new Roo.Toolbar.Fill());
11916     },
11917
11918     /**
11919      * Adds any standard HTML element to the toolbar
11920      * @param {String/HTMLElement/Element} el The element or id of the element to add
11921      * @return {Roo.Toolbar.Item} The element's item
11922      */
11923     addElement : function(el){
11924         return this.addItem(new Roo.Toolbar.Item(el));
11925     },
11926     /**
11927      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11928      * @type Roo.util.MixedCollection  
11929      */
11930     items : false,
11931      
11932     /**
11933      * Adds any Toolbar.Item or subclass
11934      * @param {Roo.Toolbar.Item} item
11935      * @return {Roo.Toolbar.Item} The item
11936      */
11937     addItem : function(item){
11938         var td = this.nextBlock();
11939         item.render(td);
11940         this.items.add(item);
11941         return item;
11942     },
11943     
11944     /**
11945      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11946      * @param {Object/Array} config A button config or array of configs
11947      * @return {Roo.Toolbar.Button/Array}
11948      */
11949     addButton : function(config){
11950         if(config instanceof Array){
11951             var buttons = [];
11952             for(var i = 0, len = config.length; i < len; i++) {
11953                 buttons.push(this.addButton(config[i]));
11954             }
11955             return buttons;
11956         }
11957         var b = config;
11958         if(!(config instanceof Roo.Toolbar.Button)){
11959             b = config.split ?
11960                 new Roo.Toolbar.SplitButton(config) :
11961                 new Roo.Toolbar.Button(config);
11962         }
11963         var td = this.nextBlock();
11964         b.render(td);
11965         this.items.add(b);
11966         return b;
11967     },
11968     
11969     /**
11970      * Adds text to the toolbar
11971      * @param {String} text The text to add
11972      * @return {Roo.Toolbar.Item} The element's item
11973      */
11974     addText : function(text){
11975         return this.addItem(new Roo.Toolbar.TextItem(text));
11976     },
11977     
11978     /**
11979      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11980      * @param {Number} index The index where the item is to be inserted
11981      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11982      * @return {Roo.Toolbar.Button/Item}
11983      */
11984     insertButton : function(index, item){
11985         if(item instanceof Array){
11986             var buttons = [];
11987             for(var i = 0, len = item.length; i < len; i++) {
11988                buttons.push(this.insertButton(index + i, item[i]));
11989             }
11990             return buttons;
11991         }
11992         if (!(item instanceof Roo.Toolbar.Button)){
11993            item = new Roo.Toolbar.Button(item);
11994         }
11995         var td = document.createElement("td");
11996         this.tr.insertBefore(td, this.tr.childNodes[index]);
11997         item.render(td);
11998         this.items.insert(index, item);
11999         return item;
12000     },
12001     
12002     /**
12003      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12004      * @param {Object} config
12005      * @return {Roo.Toolbar.Item} The element's item
12006      */
12007     addDom : function(config, returnEl){
12008         var td = this.nextBlock();
12009         Roo.DomHelper.overwrite(td, config);
12010         var ti = new Roo.Toolbar.Item(td.firstChild);
12011         ti.render(td);
12012         this.items.add(ti);
12013         return ti;
12014     },
12015
12016     /**
12017      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12018      * @type Roo.util.MixedCollection  
12019      */
12020     fields : false,
12021     
12022     /**
12023      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12024      * Note: the field should not have been rendered yet. For a field that has already been
12025      * rendered, use {@link #addElement}.
12026      * @param {Roo.form.Field} field
12027      * @return {Roo.ToolbarItem}
12028      */
12029      
12030       
12031     addField : function(field) {
12032         if (!this.fields) {
12033             var autoId = 0;
12034             this.fields = new Roo.util.MixedCollection(false, function(o){
12035                 return o.id || ("item" + (++autoId));
12036             });
12037
12038         }
12039         
12040         var td = this.nextBlock();
12041         field.render(td);
12042         var ti = new Roo.Toolbar.Item(td.firstChild);
12043         ti.render(td);
12044         this.items.add(ti);
12045         this.fields.add(field);
12046         return ti;
12047     },
12048     /**
12049      * Hide the toolbar
12050      * @method hide
12051      */
12052      
12053       
12054     hide : function()
12055     {
12056         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12057         this.el.child('div').hide();
12058     },
12059     /**
12060      * Show the toolbar
12061      * @method show
12062      */
12063     show : function()
12064     {
12065         this.el.child('div').show();
12066     },
12067       
12068     // private
12069     nextBlock : function(){
12070         var td = document.createElement("td");
12071         this.tr.appendChild(td);
12072         return td;
12073     },
12074
12075     // private
12076     destroy : function(){
12077         if(this.items){ // rendered?
12078             Roo.destroy.apply(Roo, this.items.items);
12079         }
12080         if(this.fields){ // rendered?
12081             Roo.destroy.apply(Roo, this.fields.items);
12082         }
12083         Roo.Element.uncache(this.el, this.tr);
12084     }
12085 };
12086
12087 /**
12088  * @class Roo.Toolbar.Item
12089  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12090  * @constructor
12091  * Creates a new Item
12092  * @param {HTMLElement} el 
12093  */
12094 Roo.Toolbar.Item = function(el){
12095     var cfg = {};
12096     if (typeof (el.xtype) != 'undefined') {
12097         cfg = el;
12098         el = cfg.el;
12099     }
12100     
12101     this.el = Roo.getDom(el);
12102     this.id = Roo.id(this.el);
12103     this.hidden = false;
12104     
12105     this.addEvents({
12106          /**
12107              * @event render
12108              * Fires when the button is rendered
12109              * @param {Button} this
12110              */
12111         'render': true
12112     });
12113     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12114 };
12115 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12116 //Roo.Toolbar.Item.prototype = {
12117     
12118     /**
12119      * Get this item's HTML Element
12120      * @return {HTMLElement}
12121      */
12122     getEl : function(){
12123        return this.el;  
12124     },
12125
12126     // private
12127     render : function(td){
12128         
12129          this.td = td;
12130         td.appendChild(this.el);
12131         
12132         this.fireEvent('render', this);
12133     },
12134     
12135     /**
12136      * Removes and destroys this item.
12137      */
12138     destroy : function(){
12139         this.td.parentNode.removeChild(this.td);
12140     },
12141     
12142     /**
12143      * Shows this item.
12144      */
12145     show: function(){
12146         this.hidden = false;
12147         this.td.style.display = "";
12148     },
12149     
12150     /**
12151      * Hides this item.
12152      */
12153     hide: function(){
12154         this.hidden = true;
12155         this.td.style.display = "none";
12156     },
12157     
12158     /**
12159      * Convenience function for boolean show/hide.
12160      * @param {Boolean} visible true to show/false to hide
12161      */
12162     setVisible: function(visible){
12163         if(visible) {
12164             this.show();
12165         }else{
12166             this.hide();
12167         }
12168     },
12169     
12170     /**
12171      * Try to focus this item.
12172      */
12173     focus : function(){
12174         Roo.fly(this.el).focus();
12175     },
12176     
12177     /**
12178      * Disables this item.
12179      */
12180     disable : function(){
12181         Roo.fly(this.td).addClass("x-item-disabled");
12182         this.disabled = true;
12183         this.el.disabled = true;
12184     },
12185     
12186     /**
12187      * Enables this item.
12188      */
12189     enable : function(){
12190         Roo.fly(this.td).removeClass("x-item-disabled");
12191         this.disabled = false;
12192         this.el.disabled = false;
12193     }
12194 });
12195
12196
12197 /**
12198  * @class Roo.Toolbar.Separator
12199  * @extends Roo.Toolbar.Item
12200  * A simple toolbar separator class
12201  * @constructor
12202  * Creates a new Separator
12203  */
12204 Roo.Toolbar.Separator = function(cfg){
12205     
12206     var s = document.createElement("span");
12207     s.className = "ytb-sep";
12208     if (cfg) {
12209         cfg.el = s;
12210     }
12211     
12212     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12213 };
12214 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12215     enable:Roo.emptyFn,
12216     disable:Roo.emptyFn,
12217     focus:Roo.emptyFn
12218 });
12219
12220 /**
12221  * @class Roo.Toolbar.Spacer
12222  * @extends Roo.Toolbar.Item
12223  * A simple element that adds extra horizontal space to a toolbar.
12224  * @constructor
12225  * Creates a new Spacer
12226  */
12227 Roo.Toolbar.Spacer = function(cfg){
12228     var s = document.createElement("div");
12229     s.className = "ytb-spacer";
12230     if (cfg) {
12231         cfg.el = s;
12232     }
12233     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12234 };
12235 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12236     enable:Roo.emptyFn,
12237     disable:Roo.emptyFn,
12238     focus:Roo.emptyFn
12239 });
12240
12241 /**
12242  * @class Roo.Toolbar.Fill
12243  * @extends Roo.Toolbar.Spacer
12244  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12245  * @constructor
12246  * Creates a new Spacer
12247  */
12248 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12249     // private
12250     render : function(td){
12251         td.style.width = '100%';
12252         Roo.Toolbar.Fill.superclass.render.call(this, td);
12253     }
12254 });
12255
12256 /**
12257  * @class Roo.Toolbar.TextItem
12258  * @extends Roo.Toolbar.Item
12259  * A simple class that renders text directly into a toolbar.
12260  * @constructor
12261  * Creates a new TextItem
12262  * @param {String} text
12263  */
12264 Roo.Toolbar.TextItem = function(cfg){
12265     var  text = cfg || "";
12266     if (typeof(cfg) == 'object') {
12267         text = cfg.text || "";
12268     }  else {
12269         cfg = null;
12270     }
12271     var s = document.createElement("span");
12272     s.className = "ytb-text";
12273     s.innerHTML = text;
12274     if (cfg) {
12275         cfg.el  = s;
12276     }
12277     
12278     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12279 };
12280 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12281     
12282      
12283     enable:Roo.emptyFn,
12284     disable:Roo.emptyFn,
12285     focus:Roo.emptyFn
12286 });
12287
12288 /**
12289  * @class Roo.Toolbar.Button
12290  * @extends Roo.Button
12291  * A button that renders into a toolbar.
12292  * @constructor
12293  * Creates a new Button
12294  * @param {Object} config A standard {@link Roo.Button} config object
12295  */
12296 Roo.Toolbar.Button = function(config){
12297     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12298 };
12299 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12300     render : function(td){
12301         this.td = td;
12302         Roo.Toolbar.Button.superclass.render.call(this, td);
12303     },
12304     
12305     /**
12306      * Removes and destroys this button
12307      */
12308     destroy : function(){
12309         Roo.Toolbar.Button.superclass.destroy.call(this);
12310         this.td.parentNode.removeChild(this.td);
12311     },
12312     
12313     /**
12314      * Shows this button
12315      */
12316     show: function(){
12317         this.hidden = false;
12318         this.td.style.display = "";
12319     },
12320     
12321     /**
12322      * Hides this button
12323      */
12324     hide: function(){
12325         this.hidden = true;
12326         this.td.style.display = "none";
12327     },
12328
12329     /**
12330      * Disables this item
12331      */
12332     disable : function(){
12333         Roo.fly(this.td).addClass("x-item-disabled");
12334         this.disabled = true;
12335     },
12336
12337     /**
12338      * Enables this item
12339      */
12340     enable : function(){
12341         Roo.fly(this.td).removeClass("x-item-disabled");
12342         this.disabled = false;
12343     }
12344 });
12345 // backwards compat
12346 Roo.ToolbarButton = Roo.Toolbar.Button;
12347
12348 /**
12349  * @class Roo.Toolbar.SplitButton
12350  * @extends Roo.SplitButton
12351  * A menu button that renders into a toolbar.
12352  * @constructor
12353  * Creates a new SplitButton
12354  * @param {Object} config A standard {@link Roo.SplitButton} config object
12355  */
12356 Roo.Toolbar.SplitButton = function(config){
12357     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12358 };
12359 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12360     render : function(td){
12361         this.td = td;
12362         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12363     },
12364     
12365     /**
12366      * Removes and destroys this button
12367      */
12368     destroy : function(){
12369         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12370         this.td.parentNode.removeChild(this.td);
12371     },
12372     
12373     /**
12374      * Shows this button
12375      */
12376     show: function(){
12377         this.hidden = false;
12378         this.td.style.display = "";
12379     },
12380     
12381     /**
12382      * Hides this button
12383      */
12384     hide: function(){
12385         this.hidden = true;
12386         this.td.style.display = "none";
12387     }
12388 });
12389
12390 // backwards compat
12391 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401  
12402 /**
12403  * @class Roo.PagingToolbar
12404  * @extends Roo.Toolbar
12405  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12406  * @constructor
12407  * Create a new PagingToolbar
12408  * @param {Object} config The config object
12409  */
12410 Roo.PagingToolbar = function(el, ds, config)
12411 {
12412     // old args format still supported... - xtype is prefered..
12413     if (typeof(el) == 'object' && el.xtype) {
12414         // created from xtype...
12415         config = el;
12416         ds = el.dataSource;
12417         el = config.container;
12418     }
12419     var items = [];
12420     if (config.items) {
12421         items = config.items;
12422         config.items = [];
12423     }
12424     
12425     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12426     this.ds = ds;
12427     this.cursor = 0;
12428     this.renderButtons(this.el);
12429     this.bind(ds);
12430     
12431     // supprot items array.
12432    
12433     Roo.each(items, function(e) {
12434         this.add(Roo.factory(e));
12435     },this);
12436     
12437 };
12438
12439 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12440     /**
12441      * @cfg {Roo.data.Store} dataSource
12442      * The underlying data store providing the paged data
12443      */
12444     /**
12445      * @cfg {String/HTMLElement/Element} container
12446      * container The id or element that will contain the toolbar
12447      */
12448     /**
12449      * @cfg {Boolean} displayInfo
12450      * True to display the displayMsg (defaults to false)
12451      */
12452     /**
12453      * @cfg {Number} pageSize
12454      * The number of records to display per page (defaults to 20)
12455      */
12456     pageSize: 20,
12457     /**
12458      * @cfg {String} displayMsg
12459      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12460      */
12461     displayMsg : 'Displaying {0} - {1} of {2}',
12462     /**
12463      * @cfg {String} emptyMsg
12464      * The message to display when no records are found (defaults to "No data to display")
12465      */
12466     emptyMsg : 'No data to display',
12467     /**
12468      * Customizable piece of the default paging text (defaults to "Page")
12469      * @type String
12470      */
12471     beforePageText : "Page",
12472     /**
12473      * Customizable piece of the default paging text (defaults to "of %0")
12474      * @type String
12475      */
12476     afterPageText : "of {0}",
12477     /**
12478      * Customizable piece of the default paging text (defaults to "First Page")
12479      * @type String
12480      */
12481     firstText : "First Page",
12482     /**
12483      * Customizable piece of the default paging text (defaults to "Previous Page")
12484      * @type String
12485      */
12486     prevText : "Previous Page",
12487     /**
12488      * Customizable piece of the default paging text (defaults to "Next Page")
12489      * @type String
12490      */
12491     nextText : "Next Page",
12492     /**
12493      * Customizable piece of the default paging text (defaults to "Last Page")
12494      * @type String
12495      */
12496     lastText : "Last Page",
12497     /**
12498      * Customizable piece of the default paging text (defaults to "Refresh")
12499      * @type String
12500      */
12501     refreshText : "Refresh",
12502
12503     // private
12504     renderButtons : function(el){
12505         Roo.PagingToolbar.superclass.render.call(this, el);
12506         this.first = this.addButton({
12507             tooltip: this.firstText,
12508             cls: "x-btn-icon x-grid-page-first",
12509             disabled: true,
12510             handler: this.onClick.createDelegate(this, ["first"])
12511         });
12512         this.prev = this.addButton({
12513             tooltip: this.prevText,
12514             cls: "x-btn-icon x-grid-page-prev",
12515             disabled: true,
12516             handler: this.onClick.createDelegate(this, ["prev"])
12517         });
12518         //this.addSeparator();
12519         this.add(this.beforePageText);
12520         this.field = Roo.get(this.addDom({
12521            tag: "input",
12522            type: "text",
12523            size: "3",
12524            value: "1",
12525            cls: "x-grid-page-number"
12526         }).el);
12527         this.field.on("keydown", this.onPagingKeydown, this);
12528         this.field.on("focus", function(){this.dom.select();});
12529         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12530         this.field.setHeight(18);
12531         //this.addSeparator();
12532         this.next = this.addButton({
12533             tooltip: this.nextText,
12534             cls: "x-btn-icon x-grid-page-next",
12535             disabled: true,
12536             handler: this.onClick.createDelegate(this, ["next"])
12537         });
12538         this.last = this.addButton({
12539             tooltip: this.lastText,
12540             cls: "x-btn-icon x-grid-page-last",
12541             disabled: true,
12542             handler: this.onClick.createDelegate(this, ["last"])
12543         });
12544         //this.addSeparator();
12545         this.loading = this.addButton({
12546             tooltip: this.refreshText,
12547             cls: "x-btn-icon x-grid-loading",
12548             handler: this.onClick.createDelegate(this, ["refresh"])
12549         });
12550
12551         if(this.displayInfo){
12552             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12553         }
12554     },
12555
12556     // private
12557     updateInfo : function(){
12558         if(this.displayEl){
12559             var count = this.ds.getCount();
12560             var msg = count == 0 ?
12561                 this.emptyMsg :
12562                 String.format(
12563                     this.displayMsg,
12564                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12565                 );
12566             this.displayEl.update(msg);
12567         }
12568     },
12569
12570     // private
12571     onLoad : function(ds, r, o){
12572        this.cursor = o.params ? o.params.start : 0;
12573        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12574
12575        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12576        this.field.dom.value = ap;
12577        this.first.setDisabled(ap == 1);
12578        this.prev.setDisabled(ap == 1);
12579        this.next.setDisabled(ap == ps);
12580        this.last.setDisabled(ap == ps);
12581        this.loading.enable();
12582        this.updateInfo();
12583     },
12584
12585     // private
12586     getPageData : function(){
12587         var total = this.ds.getTotalCount();
12588         return {
12589             total : total,
12590             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12591             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12592         };
12593     },
12594
12595     // private
12596     onLoadError : function(){
12597         this.loading.enable();
12598     },
12599
12600     // private
12601     onPagingKeydown : function(e){
12602         var k = e.getKey();
12603         var d = this.getPageData();
12604         if(k == e.RETURN){
12605             var v = this.field.dom.value, pageNum;
12606             if(!v || isNaN(pageNum = parseInt(v, 10))){
12607                 this.field.dom.value = d.activePage;
12608                 return;
12609             }
12610             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12611             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12612             e.stopEvent();
12613         }
12614         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))
12615         {
12616           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12617           this.field.dom.value = pageNum;
12618           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12619           e.stopEvent();
12620         }
12621         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12622         {
12623           var v = this.field.dom.value, pageNum; 
12624           var increment = (e.shiftKey) ? 10 : 1;
12625           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12626             increment *= -1;
12627           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12628             this.field.dom.value = d.activePage;
12629             return;
12630           }
12631           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12632           {
12633             this.field.dom.value = parseInt(v, 10) + increment;
12634             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12635             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12636           }
12637           e.stopEvent();
12638         }
12639     },
12640
12641     // private
12642     beforeLoad : function(){
12643         if(this.loading){
12644             this.loading.disable();
12645         }
12646     },
12647
12648     // private
12649     onClick : function(which){
12650         var ds = this.ds;
12651         switch(which){
12652             case "first":
12653                 ds.load({params:{start: 0, limit: this.pageSize}});
12654             break;
12655             case "prev":
12656                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12657             break;
12658             case "next":
12659                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12660             break;
12661             case "last":
12662                 var total = ds.getTotalCount();
12663                 var extra = total % this.pageSize;
12664                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12665                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12666             break;
12667             case "refresh":
12668                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12669             break;
12670         }
12671     },
12672
12673     /**
12674      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12675      * @param {Roo.data.Store} store The data store to unbind
12676      */
12677     unbind : function(ds){
12678         ds.un("beforeload", this.beforeLoad, this);
12679         ds.un("load", this.onLoad, this);
12680         ds.un("loadexception", this.onLoadError, this);
12681         ds.un("remove", this.updateInfo, this);
12682         ds.un("add", this.updateInfo, this);
12683         this.ds = undefined;
12684     },
12685
12686     /**
12687      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12688      * @param {Roo.data.Store} store The data store to bind
12689      */
12690     bind : function(ds){
12691         ds.on("beforeload", this.beforeLoad, this);
12692         ds.on("load", this.onLoad, this);
12693         ds.on("loadexception", this.onLoadError, this);
12694         ds.on("remove", this.updateInfo, this);
12695         ds.on("add", this.updateInfo, this);
12696         this.ds = ds;
12697     }
12698 });/*
12699  * Based on:
12700  * Ext JS Library 1.1.1
12701  * Copyright(c) 2006-2007, Ext JS, LLC.
12702  *
12703  * Originally Released Under LGPL - original licence link has changed is not relivant.
12704  *
12705  * Fork - LGPL
12706  * <script type="text/javascript">
12707  */
12708
12709 /**
12710  * @class Roo.Resizable
12711  * @extends Roo.util.Observable
12712  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12713  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12714  * 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
12715  * the element will be wrapped for you automatically.</p>
12716  * <p>Here is the list of valid resize handles:</p>
12717  * <pre>
12718 Value   Description
12719 ------  -------------------
12720  'n'     north
12721  's'     south
12722  'e'     east
12723  'w'     west
12724  'nw'    northwest
12725  'sw'    southwest
12726  'se'    southeast
12727  'ne'    northeast
12728  'hd'    horizontal drag
12729  'all'   all
12730 </pre>
12731  * <p>Here's an example showing the creation of a typical Resizable:</p>
12732  * <pre><code>
12733 var resizer = new Roo.Resizable("element-id", {
12734     handles: 'all',
12735     minWidth: 200,
12736     minHeight: 100,
12737     maxWidth: 500,
12738     maxHeight: 400,
12739     pinned: true
12740 });
12741 resizer.on("resize", myHandler);
12742 </code></pre>
12743  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12744  * resizer.east.setDisplayed(false);</p>
12745  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12746  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12747  * resize operation's new size (defaults to [0, 0])
12748  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12749  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12750  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12751  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12752  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12753  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12754  * @cfg {Number} width The width of the element in pixels (defaults to null)
12755  * @cfg {Number} height The height of the element in pixels (defaults to null)
12756  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12757  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12758  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12759  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12760  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12761  * in favor of the handles config option (defaults to false)
12762  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12763  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12764  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12765  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12766  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12767  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12768  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12769  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12770  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12771  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12772  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12773  * @constructor
12774  * Create a new resizable component
12775  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12776  * @param {Object} config configuration options
12777   */
12778 Roo.Resizable = function(el, config)
12779 {
12780     this.el = Roo.get(el);
12781
12782     if(config && config.wrap){
12783         config.resizeChild = this.el;
12784         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12785         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12786         this.el.setStyle("overflow", "hidden");
12787         this.el.setPositioning(config.resizeChild.getPositioning());
12788         config.resizeChild.clearPositioning();
12789         if(!config.width || !config.height){
12790             var csize = config.resizeChild.getSize();
12791             this.el.setSize(csize.width, csize.height);
12792         }
12793         if(config.pinned && !config.adjustments){
12794             config.adjustments = "auto";
12795         }
12796     }
12797
12798     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12799     this.proxy.unselectable();
12800     this.proxy.enableDisplayMode('block');
12801
12802     Roo.apply(this, config);
12803
12804     if(this.pinned){
12805         this.disableTrackOver = true;
12806         this.el.addClass("x-resizable-pinned");
12807     }
12808     // if the element isn't positioned, make it relative
12809     var position = this.el.getStyle("position");
12810     if(position != "absolute" && position != "fixed"){
12811         this.el.setStyle("position", "relative");
12812     }
12813     if(!this.handles){ // no handles passed, must be legacy style
12814         this.handles = 's,e,se';
12815         if(this.multiDirectional){
12816             this.handles += ',n,w';
12817         }
12818     }
12819     if(this.handles == "all"){
12820         this.handles = "n s e w ne nw se sw";
12821     }
12822     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12823     var ps = Roo.Resizable.positions;
12824     for(var i = 0, len = hs.length; i < len; i++){
12825         if(hs[i] && ps[hs[i]]){
12826             var pos = ps[hs[i]];
12827             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12828         }
12829     }
12830     // legacy
12831     this.corner = this.southeast;
12832     
12833     // updateBox = the box can move..
12834     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12835         this.updateBox = true;
12836     }
12837
12838     this.activeHandle = null;
12839
12840     if(this.resizeChild){
12841         if(typeof this.resizeChild == "boolean"){
12842             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12843         }else{
12844             this.resizeChild = Roo.get(this.resizeChild, true);
12845         }
12846     }
12847     
12848     if(this.adjustments == "auto"){
12849         var rc = this.resizeChild;
12850         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12851         if(rc && (hw || hn)){
12852             rc.position("relative");
12853             rc.setLeft(hw ? hw.el.getWidth() : 0);
12854             rc.setTop(hn ? hn.el.getHeight() : 0);
12855         }
12856         this.adjustments = [
12857             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12858             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12859         ];
12860     }
12861
12862     if(this.draggable){
12863         this.dd = this.dynamic ?
12864             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12865         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12866     }
12867
12868     // public events
12869     this.addEvents({
12870         /**
12871          * @event beforeresize
12872          * Fired before resize is allowed. Set enabled to false to cancel resize.
12873          * @param {Roo.Resizable} this
12874          * @param {Roo.EventObject} e The mousedown event
12875          */
12876         "beforeresize" : true,
12877         /**
12878          * @event resizing
12879          * Fired a resizing.
12880          * @param {Roo.Resizable} this
12881          * @param {Number} x The new x position
12882          * @param {Number} y The new y position
12883          * @param {Number} w The new w width
12884          * @param {Number} h The new h hight
12885          * @param {Roo.EventObject} e The mouseup event
12886          */
12887         "resizing" : true,
12888         /**
12889          * @event resize
12890          * Fired after a resize.
12891          * @param {Roo.Resizable} this
12892          * @param {Number} width The new width
12893          * @param {Number} height The new height
12894          * @param {Roo.EventObject} e The mouseup event
12895          */
12896         "resize" : true
12897     });
12898
12899     if(this.width !== null && this.height !== null){
12900         this.resizeTo(this.width, this.height);
12901     }else{
12902         this.updateChildSize();
12903     }
12904     if(Roo.isIE){
12905         this.el.dom.style.zoom = 1;
12906     }
12907     Roo.Resizable.superclass.constructor.call(this);
12908 };
12909
12910 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12911         resizeChild : false,
12912         adjustments : [0, 0],
12913         minWidth : 5,
12914         minHeight : 5,
12915         maxWidth : 10000,
12916         maxHeight : 10000,
12917         enabled : true,
12918         animate : false,
12919         duration : .35,
12920         dynamic : false,
12921         handles : false,
12922         multiDirectional : false,
12923         disableTrackOver : false,
12924         easing : 'easeOutStrong',
12925         widthIncrement : 0,
12926         heightIncrement : 0,
12927         pinned : false,
12928         width : null,
12929         height : null,
12930         preserveRatio : false,
12931         transparent: false,
12932         minX: 0,
12933         minY: 0,
12934         draggable: false,
12935
12936         /**
12937          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12938          */
12939         constrainTo: undefined,
12940         /**
12941          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12942          */
12943         resizeRegion: undefined,
12944
12945
12946     /**
12947      * Perform a manual resize
12948      * @param {Number} width
12949      * @param {Number} height
12950      */
12951     resizeTo : function(width, height){
12952         this.el.setSize(width, height);
12953         this.updateChildSize();
12954         this.fireEvent("resize", this, width, height, null);
12955     },
12956
12957     // private
12958     startSizing : function(e, handle){
12959         this.fireEvent("beforeresize", this, e);
12960         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12961
12962             if(!this.overlay){
12963                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12964                 this.overlay.unselectable();
12965                 this.overlay.enableDisplayMode("block");
12966                 this.overlay.on("mousemove", this.onMouseMove, this);
12967                 this.overlay.on("mouseup", this.onMouseUp, this);
12968             }
12969             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12970
12971             this.resizing = true;
12972             this.startBox = this.el.getBox();
12973             this.startPoint = e.getXY();
12974             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12975                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12976
12977             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12978             this.overlay.show();
12979
12980             if(this.constrainTo) {
12981                 var ct = Roo.get(this.constrainTo);
12982                 this.resizeRegion = ct.getRegion().adjust(
12983                     ct.getFrameWidth('t'),
12984                     ct.getFrameWidth('l'),
12985                     -ct.getFrameWidth('b'),
12986                     -ct.getFrameWidth('r')
12987                 );
12988             }
12989
12990             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12991             this.proxy.show();
12992             this.proxy.setBox(this.startBox);
12993             if(!this.dynamic){
12994                 this.proxy.setStyle('visibility', 'visible');
12995             }
12996         }
12997     },
12998
12999     // private
13000     onMouseDown : function(handle, e){
13001         if(this.enabled){
13002             e.stopEvent();
13003             this.activeHandle = handle;
13004             this.startSizing(e, handle);
13005         }
13006     },
13007
13008     // private
13009     onMouseUp : function(e){
13010         var size = this.resizeElement();
13011         this.resizing = false;
13012         this.handleOut();
13013         this.overlay.hide();
13014         this.proxy.hide();
13015         this.fireEvent("resize", this, size.width, size.height, e);
13016     },
13017
13018     // private
13019     updateChildSize : function(){
13020         
13021         if(this.resizeChild){
13022             var el = this.el;
13023             var child = this.resizeChild;
13024             var adj = this.adjustments;
13025             if(el.dom.offsetWidth){
13026                 var b = el.getSize(true);
13027                 child.setSize(b.width+adj[0], b.height+adj[1]);
13028             }
13029             // Second call here for IE
13030             // The first call enables instant resizing and
13031             // the second call corrects scroll bars if they
13032             // exist
13033             if(Roo.isIE){
13034                 setTimeout(function(){
13035                     if(el.dom.offsetWidth){
13036                         var b = el.getSize(true);
13037                         child.setSize(b.width+adj[0], b.height+adj[1]);
13038                     }
13039                 }, 10);
13040             }
13041         }
13042     },
13043
13044     // private
13045     snap : function(value, inc, min){
13046         if(!inc || !value) return value;
13047         var newValue = value;
13048         var m = value % inc;
13049         if(m > 0){
13050             if(m > (inc/2)){
13051                 newValue = value + (inc-m);
13052             }else{
13053                 newValue = value - m;
13054             }
13055         }
13056         return Math.max(min, newValue);
13057     },
13058
13059     // private
13060     resizeElement : function(){
13061         var box = this.proxy.getBox();
13062         if(this.updateBox){
13063             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13064         }else{
13065             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13066         }
13067         this.updateChildSize();
13068         if(!this.dynamic){
13069             this.proxy.hide();
13070         }
13071         return box;
13072     },
13073
13074     // private
13075     constrain : function(v, diff, m, mx){
13076         if(v - diff < m){
13077             diff = v - m;
13078         }else if(v - diff > mx){
13079             diff = mx - v;
13080         }
13081         return diff;
13082     },
13083
13084     // private
13085     onMouseMove : function(e){
13086         
13087         if(this.enabled){
13088             try{// try catch so if something goes wrong the user doesn't get hung
13089
13090             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13091                 return;
13092             }
13093
13094             //var curXY = this.startPoint;
13095             var curSize = this.curSize || this.startBox;
13096             var x = this.startBox.x, y = this.startBox.y;
13097             var ox = x, oy = y;
13098             var w = curSize.width, h = curSize.height;
13099             var ow = w, oh = h;
13100             var mw = this.minWidth, mh = this.minHeight;
13101             var mxw = this.maxWidth, mxh = this.maxHeight;
13102             var wi = this.widthIncrement;
13103             var hi = this.heightIncrement;
13104
13105             var eventXY = e.getXY();
13106             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13107             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13108
13109             var pos = this.activeHandle.position;
13110
13111             switch(pos){
13112                 case "east":
13113                     w += diffX;
13114                     w = Math.min(Math.max(mw, w), mxw);
13115                     break;
13116              
13117                 case "south":
13118                     h += diffY;
13119                     h = Math.min(Math.max(mh, h), mxh);
13120                     break;
13121                 case "southeast":
13122                     w += diffX;
13123                     h += diffY;
13124                     w = Math.min(Math.max(mw, w), mxw);
13125                     h = Math.min(Math.max(mh, h), mxh);
13126                     break;
13127                 case "north":
13128                     diffY = this.constrain(h, diffY, mh, mxh);
13129                     y += diffY;
13130                     h -= diffY;
13131                     break;
13132                 case "hdrag":
13133                     
13134                     if (wi) {
13135                         var adiffX = Math.abs(diffX);
13136                         var sub = (adiffX % wi); // how much 
13137                         if (sub > (wi/2)) { // far enough to snap
13138                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13139                         } else {
13140                             // remove difference.. 
13141                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13142                         }
13143                     }
13144                     x += diffX;
13145                     x = Math.max(this.minX, x);
13146                     break;
13147                 case "west":
13148                     diffX = this.constrain(w, diffX, mw, mxw);
13149                     x += diffX;
13150                     w -= diffX;
13151                     break;
13152                 case "northeast":
13153                     w += diffX;
13154                     w = Math.min(Math.max(mw, w), mxw);
13155                     diffY = this.constrain(h, diffY, mh, mxh);
13156                     y += diffY;
13157                     h -= diffY;
13158                     break;
13159                 case "northwest":
13160                     diffX = this.constrain(w, diffX, mw, mxw);
13161                     diffY = this.constrain(h, diffY, mh, mxh);
13162                     y += diffY;
13163                     h -= diffY;
13164                     x += diffX;
13165                     w -= diffX;
13166                     break;
13167                case "southwest":
13168                     diffX = this.constrain(w, diffX, mw, mxw);
13169                     h += diffY;
13170                     h = Math.min(Math.max(mh, h), mxh);
13171                     x += diffX;
13172                     w -= diffX;
13173                     break;
13174             }
13175
13176             var sw = this.snap(w, wi, mw);
13177             var sh = this.snap(h, hi, mh);
13178             if(sw != w || sh != h){
13179                 switch(pos){
13180                     case "northeast":
13181                         y -= sh - h;
13182                     break;
13183                     case "north":
13184                         y -= sh - h;
13185                         break;
13186                     case "southwest":
13187                         x -= sw - w;
13188                     break;
13189                     case "west":
13190                         x -= sw - w;
13191                         break;
13192                     case "northwest":
13193                         x -= sw - w;
13194                         y -= sh - h;
13195                     break;
13196                 }
13197                 w = sw;
13198                 h = sh;
13199             }
13200
13201             if(this.preserveRatio){
13202                 switch(pos){
13203                     case "southeast":
13204                     case "east":
13205                         h = oh * (w/ow);
13206                         h = Math.min(Math.max(mh, h), mxh);
13207                         w = ow * (h/oh);
13208                        break;
13209                     case "south":
13210                         w = ow * (h/oh);
13211                         w = Math.min(Math.max(mw, w), mxw);
13212                         h = oh * (w/ow);
13213                         break;
13214                     case "northeast":
13215                         w = ow * (h/oh);
13216                         w = Math.min(Math.max(mw, w), mxw);
13217                         h = oh * (w/ow);
13218                     break;
13219                     case "north":
13220                         var tw = w;
13221                         w = ow * (h/oh);
13222                         w = Math.min(Math.max(mw, w), mxw);
13223                         h = oh * (w/ow);
13224                         x += (tw - w) / 2;
13225                         break;
13226                     case "southwest":
13227                         h = oh * (w/ow);
13228                         h = Math.min(Math.max(mh, h), mxh);
13229                         var tw = w;
13230                         w = ow * (h/oh);
13231                         x += tw - w;
13232                         break;
13233                     case "west":
13234                         var th = h;
13235                         h = oh * (w/ow);
13236                         h = Math.min(Math.max(mh, h), mxh);
13237                         y += (th - h) / 2;
13238                         var tw = w;
13239                         w = ow * (h/oh);
13240                         x += tw - w;
13241                        break;
13242                     case "northwest":
13243                         var tw = w;
13244                         var th = h;
13245                         h = oh * (w/ow);
13246                         h = Math.min(Math.max(mh, h), mxh);
13247                         w = ow * (h/oh);
13248                         y += th - h;
13249                         x += tw - w;
13250                        break;
13251
13252                 }
13253             }
13254             if (pos == 'hdrag') {
13255                 w = ow;
13256             }
13257             this.proxy.setBounds(x, y, w, h);
13258             if(this.dynamic){
13259                 this.resizeElement();
13260             }
13261             }catch(e){}
13262         }
13263         this.fireEvent("resizing", this, x, y, w, h, e);
13264     },
13265
13266     // private
13267     handleOver : function(){
13268         if(this.enabled){
13269             this.el.addClass("x-resizable-over");
13270         }
13271     },
13272
13273     // private
13274     handleOut : function(){
13275         if(!this.resizing){
13276             this.el.removeClass("x-resizable-over");
13277         }
13278     },
13279
13280     /**
13281      * Returns the element this component is bound to.
13282      * @return {Roo.Element}
13283      */
13284     getEl : function(){
13285         return this.el;
13286     },
13287
13288     /**
13289      * Returns the resizeChild element (or null).
13290      * @return {Roo.Element}
13291      */
13292     getResizeChild : function(){
13293         return this.resizeChild;
13294     },
13295     groupHandler : function()
13296     {
13297         
13298     },
13299     /**
13300      * Destroys this resizable. If the element was wrapped and
13301      * removeEl is not true then the element remains.
13302      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13303      */
13304     destroy : function(removeEl){
13305         this.proxy.remove();
13306         if(this.overlay){
13307             this.overlay.removeAllListeners();
13308             this.overlay.remove();
13309         }
13310         var ps = Roo.Resizable.positions;
13311         for(var k in ps){
13312             if(typeof ps[k] != "function" && this[ps[k]]){
13313                 var h = this[ps[k]];
13314                 h.el.removeAllListeners();
13315                 h.el.remove();
13316             }
13317         }
13318         if(removeEl){
13319             this.el.update("");
13320             this.el.remove();
13321         }
13322     }
13323 });
13324
13325 // private
13326 // hash to map config positions to true positions
13327 Roo.Resizable.positions = {
13328     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13329     hd: "hdrag"
13330 };
13331
13332 // private
13333 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13334     if(!this.tpl){
13335         // only initialize the template if resizable is used
13336         var tpl = Roo.DomHelper.createTemplate(
13337             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13338         );
13339         tpl.compile();
13340         Roo.Resizable.Handle.prototype.tpl = tpl;
13341     }
13342     this.position = pos;
13343     this.rz = rz;
13344     // show north drag fro topdra
13345     var handlepos = pos == 'hdrag' ? 'north' : pos;
13346     
13347     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13348     if (pos == 'hdrag') {
13349         this.el.setStyle('cursor', 'pointer');
13350     }
13351     this.el.unselectable();
13352     if(transparent){
13353         this.el.setOpacity(0);
13354     }
13355     this.el.on("mousedown", this.onMouseDown, this);
13356     if(!disableTrackOver){
13357         this.el.on("mouseover", this.onMouseOver, this);
13358         this.el.on("mouseout", this.onMouseOut, this);
13359     }
13360 };
13361
13362 // private
13363 Roo.Resizable.Handle.prototype = {
13364     afterResize : function(rz){
13365         Roo.log('after?');
13366         // do nothing
13367     },
13368     // private
13369     onMouseDown : function(e){
13370         this.rz.onMouseDown(this, e);
13371     },
13372     // private
13373     onMouseOver : function(e){
13374         this.rz.handleOver(this, e);
13375     },
13376     // private
13377     onMouseOut : function(e){
13378         this.rz.handleOut(this, e);
13379     }
13380 };/*
13381  * Based on:
13382  * Ext JS Library 1.1.1
13383  * Copyright(c) 2006-2007, Ext JS, LLC.
13384  *
13385  * Originally Released Under LGPL - original licence link has changed is not relivant.
13386  *
13387  * Fork - LGPL
13388  * <script type="text/javascript">
13389  */
13390
13391 /**
13392  * @class Roo.Editor
13393  * @extends Roo.Component
13394  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13395  * @constructor
13396  * Create a new Editor
13397  * @param {Roo.form.Field} field The Field object (or descendant)
13398  * @param {Object} config The config object
13399  */
13400 Roo.Editor = function(field, config){
13401     Roo.Editor.superclass.constructor.call(this, config);
13402     this.field = field;
13403     this.addEvents({
13404         /**
13405              * @event beforestartedit
13406              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13407              * false from the handler of this event.
13408              * @param {Editor} this
13409              * @param {Roo.Element} boundEl The underlying element bound to this editor
13410              * @param {Mixed} value The field value being set
13411              */
13412         "beforestartedit" : true,
13413         /**
13414              * @event startedit
13415              * Fires when this editor is displayed
13416              * @param {Roo.Element} boundEl The underlying element bound to this editor
13417              * @param {Mixed} value The starting field value
13418              */
13419         "startedit" : true,
13420         /**
13421              * @event beforecomplete
13422              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13423              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13424              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13425              * event will not fire since no edit actually occurred.
13426              * @param {Editor} this
13427              * @param {Mixed} value The current field value
13428              * @param {Mixed} startValue The original field value
13429              */
13430         "beforecomplete" : true,
13431         /**
13432              * @event complete
13433              * Fires after editing is complete and any changed value has been written to the underlying field.
13434              * @param {Editor} this
13435              * @param {Mixed} value The current field value
13436              * @param {Mixed} startValue The original field value
13437              */
13438         "complete" : true,
13439         /**
13440          * @event specialkey
13441          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13442          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13443          * @param {Roo.form.Field} this
13444          * @param {Roo.EventObject} e The event object
13445          */
13446         "specialkey" : true
13447     });
13448 };
13449
13450 Roo.extend(Roo.Editor, Roo.Component, {
13451     /**
13452      * @cfg {Boolean/String} autosize
13453      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13454      * or "height" to adopt the height only (defaults to false)
13455      */
13456     /**
13457      * @cfg {Boolean} revertInvalid
13458      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13459      * validation fails (defaults to true)
13460      */
13461     /**
13462      * @cfg {Boolean} ignoreNoChange
13463      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13464      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13465      * will never be ignored.
13466      */
13467     /**
13468      * @cfg {Boolean} hideEl
13469      * False to keep the bound element visible while the editor is displayed (defaults to true)
13470      */
13471     /**
13472      * @cfg {Mixed} value
13473      * The data value of the underlying field (defaults to "")
13474      */
13475     value : "",
13476     /**
13477      * @cfg {String} alignment
13478      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13479      */
13480     alignment: "c-c?",
13481     /**
13482      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13483      * for bottom-right shadow (defaults to "frame")
13484      */
13485     shadow : "frame",
13486     /**
13487      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13488      */
13489     constrain : false,
13490     /**
13491      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13492      */
13493     completeOnEnter : false,
13494     /**
13495      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13496      */
13497     cancelOnEsc : false,
13498     /**
13499      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13500      */
13501     updateEl : false,
13502
13503     // private
13504     onRender : function(ct, position){
13505         this.el = new Roo.Layer({
13506             shadow: this.shadow,
13507             cls: "x-editor",
13508             parentEl : ct,
13509             shim : this.shim,
13510             shadowOffset:4,
13511             id: this.id,
13512             constrain: this.constrain
13513         });
13514         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13515         if(this.field.msgTarget != 'title'){
13516             this.field.msgTarget = 'qtip';
13517         }
13518         this.field.render(this.el);
13519         if(Roo.isGecko){
13520             this.field.el.dom.setAttribute('autocomplete', 'off');
13521         }
13522         this.field.on("specialkey", this.onSpecialKey, this);
13523         if(this.swallowKeys){
13524             this.field.el.swallowEvent(['keydown','keypress']);
13525         }
13526         this.field.show();
13527         this.field.on("blur", this.onBlur, this);
13528         if(this.field.grow){
13529             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13530         }
13531     },
13532
13533     onSpecialKey : function(field, e)
13534     {
13535         //Roo.log('editor onSpecialKey');
13536         if(this.completeOnEnter && e.getKey() == e.ENTER){
13537             e.stopEvent();
13538             this.completeEdit();
13539             return;
13540         }
13541         // do not fire special key otherwise it might hide close the editor...
13542         if(e.getKey() == e.ENTER){    
13543             return;
13544         }
13545         if(this.cancelOnEsc && e.getKey() == e.ESC){
13546             this.cancelEdit();
13547             return;
13548         } 
13549         this.fireEvent('specialkey', field, e);
13550     
13551     },
13552
13553     /**
13554      * Starts the editing process and shows the editor.
13555      * @param {String/HTMLElement/Element} el The element to edit
13556      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13557       * to the innerHTML of el.
13558      */
13559     startEdit : function(el, value){
13560         if(this.editing){
13561             this.completeEdit();
13562         }
13563         this.boundEl = Roo.get(el);
13564         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13565         if(!this.rendered){
13566             this.render(this.parentEl || document.body);
13567         }
13568         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13569             return;
13570         }
13571         this.startValue = v;
13572         this.field.setValue(v);
13573         if(this.autoSize){
13574             var sz = this.boundEl.getSize();
13575             switch(this.autoSize){
13576                 case "width":
13577                 this.setSize(sz.width,  "");
13578                 break;
13579                 case "height":
13580                 this.setSize("",  sz.height);
13581                 break;
13582                 default:
13583                 this.setSize(sz.width,  sz.height);
13584             }
13585         }
13586         this.el.alignTo(this.boundEl, this.alignment);
13587         this.editing = true;
13588         if(Roo.QuickTips){
13589             Roo.QuickTips.disable();
13590         }
13591         this.show();
13592     },
13593
13594     /**
13595      * Sets the height and width of this editor.
13596      * @param {Number} width The new width
13597      * @param {Number} height The new height
13598      */
13599     setSize : function(w, h){
13600         this.field.setSize(w, h);
13601         if(this.el){
13602             this.el.sync();
13603         }
13604     },
13605
13606     /**
13607      * Realigns the editor to the bound field based on the current alignment config value.
13608      */
13609     realign : function(){
13610         this.el.alignTo(this.boundEl, this.alignment);
13611     },
13612
13613     /**
13614      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13615      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13616      */
13617     completeEdit : function(remainVisible){
13618         if(!this.editing){
13619             return;
13620         }
13621         var v = this.getValue();
13622         if(this.revertInvalid !== false && !this.field.isValid()){
13623             v = this.startValue;
13624             this.cancelEdit(true);
13625         }
13626         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13627             this.editing = false;
13628             this.hide();
13629             return;
13630         }
13631         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13632             this.editing = false;
13633             if(this.updateEl && this.boundEl){
13634                 this.boundEl.update(v);
13635             }
13636             if(remainVisible !== true){
13637                 this.hide();
13638             }
13639             this.fireEvent("complete", this, v, this.startValue);
13640         }
13641     },
13642
13643     // private
13644     onShow : function(){
13645         this.el.show();
13646         if(this.hideEl !== false){
13647             this.boundEl.hide();
13648         }
13649         this.field.show();
13650         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13651             this.fixIEFocus = true;
13652             this.deferredFocus.defer(50, this);
13653         }else{
13654             this.field.focus();
13655         }
13656         this.fireEvent("startedit", this.boundEl, this.startValue);
13657     },
13658
13659     deferredFocus : function(){
13660         if(this.editing){
13661             this.field.focus();
13662         }
13663     },
13664
13665     /**
13666      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13667      * reverted to the original starting value.
13668      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13669      * cancel (defaults to false)
13670      */
13671     cancelEdit : function(remainVisible){
13672         if(this.editing){
13673             this.setValue(this.startValue);
13674             if(remainVisible !== true){
13675                 this.hide();
13676             }
13677         }
13678     },
13679
13680     // private
13681     onBlur : function(){
13682         if(this.allowBlur !== true && this.editing){
13683             this.completeEdit();
13684         }
13685     },
13686
13687     // private
13688     onHide : function(){
13689         if(this.editing){
13690             this.completeEdit();
13691             return;
13692         }
13693         this.field.blur();
13694         if(this.field.collapse){
13695             this.field.collapse();
13696         }
13697         this.el.hide();
13698         if(this.hideEl !== false){
13699             this.boundEl.show();
13700         }
13701         if(Roo.QuickTips){
13702             Roo.QuickTips.enable();
13703         }
13704     },
13705
13706     /**
13707      * Sets the data value of the editor
13708      * @param {Mixed} value Any valid value supported by the underlying field
13709      */
13710     setValue : function(v){
13711         this.field.setValue(v);
13712     },
13713
13714     /**
13715      * Gets the data value of the editor
13716      * @return {Mixed} The data value
13717      */
13718     getValue : function(){
13719         return this.field.getValue();
13720     }
13721 });/*
13722  * Based on:
13723  * Ext JS Library 1.1.1
13724  * Copyright(c) 2006-2007, Ext JS, LLC.
13725  *
13726  * Originally Released Under LGPL - original licence link has changed is not relivant.
13727  *
13728  * Fork - LGPL
13729  * <script type="text/javascript">
13730  */
13731  
13732 /**
13733  * @class Roo.BasicDialog
13734  * @extends Roo.util.Observable
13735  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13736  * <pre><code>
13737 var dlg = new Roo.BasicDialog("my-dlg", {
13738     height: 200,
13739     width: 300,
13740     minHeight: 100,
13741     minWidth: 150,
13742     modal: true,
13743     proxyDrag: true,
13744     shadow: true
13745 });
13746 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13747 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13748 dlg.addButton('Cancel', dlg.hide, dlg);
13749 dlg.show();
13750 </code></pre>
13751   <b>A Dialog should always be a direct child of the body element.</b>
13752  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13753  * @cfg {String} title Default text to display in the title bar (defaults to null)
13754  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13755  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13756  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13757  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13758  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13759  * (defaults to null with no animation)
13760  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13761  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13762  * property for valid values (defaults to 'all')
13763  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13764  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13765  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13766  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13767  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13768  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13769  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13770  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13771  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13772  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13773  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13774  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13775  * draggable = true (defaults to false)
13776  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13777  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13778  * shadow (defaults to false)
13779  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13780  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13781  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13782  * @cfg {Array} buttons Array of buttons
13783  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13784  * @constructor
13785  * Create a new BasicDialog.
13786  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13787  * @param {Object} config Configuration options
13788  */
13789 Roo.BasicDialog = function(el, config){
13790     this.el = Roo.get(el);
13791     var dh = Roo.DomHelper;
13792     if(!this.el && config && config.autoCreate){
13793         if(typeof config.autoCreate == "object"){
13794             if(!config.autoCreate.id){
13795                 config.autoCreate.id = el;
13796             }
13797             this.el = dh.append(document.body,
13798                         config.autoCreate, true);
13799         }else{
13800             this.el = dh.append(document.body,
13801                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13802         }
13803     }
13804     el = this.el;
13805     el.setDisplayed(true);
13806     el.hide = this.hideAction;
13807     this.id = el.id;
13808     el.addClass("x-dlg");
13809
13810     Roo.apply(this, config);
13811
13812     this.proxy = el.createProxy("x-dlg-proxy");
13813     this.proxy.hide = this.hideAction;
13814     this.proxy.setOpacity(.5);
13815     this.proxy.hide();
13816
13817     if(config.width){
13818         el.setWidth(config.width);
13819     }
13820     if(config.height){
13821         el.setHeight(config.height);
13822     }
13823     this.size = el.getSize();
13824     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13825         this.xy = [config.x,config.y];
13826     }else{
13827         this.xy = el.getCenterXY(true);
13828     }
13829     /** The header element @type Roo.Element */
13830     this.header = el.child("> .x-dlg-hd");
13831     /** The body element @type Roo.Element */
13832     this.body = el.child("> .x-dlg-bd");
13833     /** The footer element @type Roo.Element */
13834     this.footer = el.child("> .x-dlg-ft");
13835
13836     if(!this.header){
13837         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13838     }
13839     if(!this.body){
13840         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13841     }
13842
13843     this.header.unselectable();
13844     if(this.title){
13845         this.header.update(this.title);
13846     }
13847     // this element allows the dialog to be focused for keyboard event
13848     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13849     this.focusEl.swallowEvent("click", true);
13850
13851     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13852
13853     // wrap the body and footer for special rendering
13854     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13855     if(this.footer){
13856         this.bwrap.dom.appendChild(this.footer.dom);
13857     }
13858
13859     this.bg = this.el.createChild({
13860         tag: "div", cls:"x-dlg-bg",
13861         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13862     });
13863     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13864
13865
13866     if(this.autoScroll !== false && !this.autoTabs){
13867         this.body.setStyle("overflow", "auto");
13868     }
13869
13870     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13871
13872     if(this.closable !== false){
13873         this.el.addClass("x-dlg-closable");
13874         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13875         this.close.on("click", this.closeClick, this);
13876         this.close.addClassOnOver("x-dlg-close-over");
13877     }
13878     if(this.collapsible !== false){
13879         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13880         this.collapseBtn.on("click", this.collapseClick, this);
13881         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13882         this.header.on("dblclick", this.collapseClick, this);
13883     }
13884     if(this.resizable !== false){
13885         this.el.addClass("x-dlg-resizable");
13886         this.resizer = new Roo.Resizable(el, {
13887             minWidth: this.minWidth || 80,
13888             minHeight:this.minHeight || 80,
13889             handles: this.resizeHandles || "all",
13890             pinned: true
13891         });
13892         this.resizer.on("beforeresize", this.beforeResize, this);
13893         this.resizer.on("resize", this.onResize, this);
13894     }
13895     if(this.draggable !== false){
13896         el.addClass("x-dlg-draggable");
13897         if (!this.proxyDrag) {
13898             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13899         }
13900         else {
13901             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13902         }
13903         dd.setHandleElId(this.header.id);
13904         dd.endDrag = this.endMove.createDelegate(this);
13905         dd.startDrag = this.startMove.createDelegate(this);
13906         dd.onDrag = this.onDrag.createDelegate(this);
13907         dd.scroll = false;
13908         this.dd = dd;
13909     }
13910     if(this.modal){
13911         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13912         this.mask.enableDisplayMode("block");
13913         this.mask.hide();
13914         this.el.addClass("x-dlg-modal");
13915     }
13916     if(this.shadow){
13917         this.shadow = new Roo.Shadow({
13918             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13919             offset : this.shadowOffset
13920         });
13921     }else{
13922         this.shadowOffset = 0;
13923     }
13924     if(Roo.useShims && this.shim !== false){
13925         this.shim = this.el.createShim();
13926         this.shim.hide = this.hideAction;
13927         this.shim.hide();
13928     }else{
13929         this.shim = false;
13930     }
13931     if(this.autoTabs){
13932         this.initTabs();
13933     }
13934     if (this.buttons) { 
13935         var bts= this.buttons;
13936         this.buttons = [];
13937         Roo.each(bts, function(b) {
13938             this.addButton(b);
13939         }, this);
13940     }
13941     
13942     
13943     this.addEvents({
13944         /**
13945          * @event keydown
13946          * Fires when a key is pressed
13947          * @param {Roo.BasicDialog} this
13948          * @param {Roo.EventObject} e
13949          */
13950         "keydown" : true,
13951         /**
13952          * @event move
13953          * Fires when this dialog is moved by the user.
13954          * @param {Roo.BasicDialog} this
13955          * @param {Number} x The new page X
13956          * @param {Number} y The new page Y
13957          */
13958         "move" : true,
13959         /**
13960          * @event resize
13961          * Fires when this dialog is resized by the user.
13962          * @param {Roo.BasicDialog} this
13963          * @param {Number} width The new width
13964          * @param {Number} height The new height
13965          */
13966         "resize" : true,
13967         /**
13968          * @event beforehide
13969          * Fires before this dialog is hidden.
13970          * @param {Roo.BasicDialog} this
13971          */
13972         "beforehide" : true,
13973         /**
13974          * @event hide
13975          * Fires when this dialog is hidden.
13976          * @param {Roo.BasicDialog} this
13977          */
13978         "hide" : true,
13979         /**
13980          * @event beforeshow
13981          * Fires before this dialog is shown.
13982          * @param {Roo.BasicDialog} this
13983          */
13984         "beforeshow" : true,
13985         /**
13986          * @event show
13987          * Fires when this dialog is shown.
13988          * @param {Roo.BasicDialog} this
13989          */
13990         "show" : true
13991     });
13992     el.on("keydown", this.onKeyDown, this);
13993     el.on("mousedown", this.toFront, this);
13994     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13995     this.el.hide();
13996     Roo.DialogManager.register(this);
13997     Roo.BasicDialog.superclass.constructor.call(this);
13998 };
13999
14000 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14001     shadowOffset: Roo.isIE ? 6 : 5,
14002     minHeight: 80,
14003     minWidth: 200,
14004     minButtonWidth: 75,
14005     defaultButton: null,
14006     buttonAlign: "right",
14007     tabTag: 'div',
14008     firstShow: true,
14009
14010     /**
14011      * Sets the dialog title text
14012      * @param {String} text The title text to display
14013      * @return {Roo.BasicDialog} this
14014      */
14015     setTitle : function(text){
14016         this.header.update(text);
14017         return this;
14018     },
14019
14020     // private
14021     closeClick : function(){
14022         this.hide();
14023     },
14024
14025     // private
14026     collapseClick : function(){
14027         this[this.collapsed ? "expand" : "collapse"]();
14028     },
14029
14030     /**
14031      * Collapses the dialog to its minimized state (only the title bar is visible).
14032      * Equivalent to the user clicking the collapse dialog button.
14033      */
14034     collapse : function(){
14035         if(!this.collapsed){
14036             this.collapsed = true;
14037             this.el.addClass("x-dlg-collapsed");
14038             this.restoreHeight = this.el.getHeight();
14039             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14040         }
14041     },
14042
14043     /**
14044      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14045      * clicking the expand dialog button.
14046      */
14047     expand : function(){
14048         if(this.collapsed){
14049             this.collapsed = false;
14050             this.el.removeClass("x-dlg-collapsed");
14051             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14052         }
14053     },
14054
14055     /**
14056      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14057      * @return {Roo.TabPanel} The tabs component
14058      */
14059     initTabs : function(){
14060         var tabs = this.getTabs();
14061         while(tabs.getTab(0)){
14062             tabs.removeTab(0);
14063         }
14064         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14065             var dom = el.dom;
14066             tabs.addTab(Roo.id(dom), dom.title);
14067             dom.title = "";
14068         });
14069         tabs.activate(0);
14070         return tabs;
14071     },
14072
14073     // private
14074     beforeResize : function(){
14075         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14076     },
14077
14078     // private
14079     onResize : function(){
14080         this.refreshSize();
14081         this.syncBodyHeight();
14082         this.adjustAssets();
14083         this.focus();
14084         this.fireEvent("resize", this, this.size.width, this.size.height);
14085     },
14086
14087     // private
14088     onKeyDown : function(e){
14089         if(this.isVisible()){
14090             this.fireEvent("keydown", this, e);
14091         }
14092     },
14093
14094     /**
14095      * Resizes the dialog.
14096      * @param {Number} width
14097      * @param {Number} height
14098      * @return {Roo.BasicDialog} this
14099      */
14100     resizeTo : function(width, height){
14101         this.el.setSize(width, height);
14102         this.size = {width: width, height: height};
14103         this.syncBodyHeight();
14104         if(this.fixedcenter){
14105             this.center();
14106         }
14107         if(this.isVisible()){
14108             this.constrainXY();
14109             this.adjustAssets();
14110         }
14111         this.fireEvent("resize", this, width, height);
14112         return this;
14113     },
14114
14115
14116     /**
14117      * Resizes the dialog to fit the specified content size.
14118      * @param {Number} width
14119      * @param {Number} height
14120      * @return {Roo.BasicDialog} this
14121      */
14122     setContentSize : function(w, h){
14123         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14124         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14125         //if(!this.el.isBorderBox()){
14126             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14127             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14128         //}
14129         if(this.tabs){
14130             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14131             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14132         }
14133         this.resizeTo(w, h);
14134         return this;
14135     },
14136
14137     /**
14138      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14139      * executed in response to a particular key being pressed while the dialog is active.
14140      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14141      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14142      * @param {Function} fn The function to call
14143      * @param {Object} scope (optional) The scope of the function
14144      * @return {Roo.BasicDialog} this
14145      */
14146     addKeyListener : function(key, fn, scope){
14147         var keyCode, shift, ctrl, alt;
14148         if(typeof key == "object" && !(key instanceof Array)){
14149             keyCode = key["key"];
14150             shift = key["shift"];
14151             ctrl = key["ctrl"];
14152             alt = key["alt"];
14153         }else{
14154             keyCode = key;
14155         }
14156         var handler = function(dlg, e){
14157             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14158                 var k = e.getKey();
14159                 if(keyCode instanceof Array){
14160                     for(var i = 0, len = keyCode.length; i < len; i++){
14161                         if(keyCode[i] == k){
14162                           fn.call(scope || window, dlg, k, e);
14163                           return;
14164                         }
14165                     }
14166                 }else{
14167                     if(k == keyCode){
14168                         fn.call(scope || window, dlg, k, e);
14169                     }
14170                 }
14171             }
14172         };
14173         this.on("keydown", handler);
14174         return this;
14175     },
14176
14177     /**
14178      * Returns the TabPanel component (creates it if it doesn't exist).
14179      * Note: If you wish to simply check for the existence of tabs without creating them,
14180      * check for a null 'tabs' property.
14181      * @return {Roo.TabPanel} The tabs component
14182      */
14183     getTabs : function(){
14184         if(!this.tabs){
14185             this.el.addClass("x-dlg-auto-tabs");
14186             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14187             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14188         }
14189         return this.tabs;
14190     },
14191
14192     /**
14193      * Adds a button to the footer section of the dialog.
14194      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14195      * object or a valid Roo.DomHelper element config
14196      * @param {Function} handler The function called when the button is clicked
14197      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14198      * @return {Roo.Button} The new button
14199      */
14200     addButton : function(config, handler, scope){
14201         var dh = Roo.DomHelper;
14202         if(!this.footer){
14203             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14204         }
14205         if(!this.btnContainer){
14206             var tb = this.footer.createChild({
14207
14208                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14209                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14210             }, null, true);
14211             this.btnContainer = tb.firstChild.firstChild.firstChild;
14212         }
14213         var bconfig = {
14214             handler: handler,
14215             scope: scope,
14216             minWidth: this.minButtonWidth,
14217             hideParent:true
14218         };
14219         if(typeof config == "string"){
14220             bconfig.text = config;
14221         }else{
14222             if(config.tag){
14223                 bconfig.dhconfig = config;
14224             }else{
14225                 Roo.apply(bconfig, config);
14226             }
14227         }
14228         var fc = false;
14229         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14230             bconfig.position = Math.max(0, bconfig.position);
14231             fc = this.btnContainer.childNodes[bconfig.position];
14232         }
14233          
14234         var btn = new Roo.Button(
14235             fc ? 
14236                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14237                 : this.btnContainer.appendChild(document.createElement("td")),
14238             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14239             bconfig
14240         );
14241         this.syncBodyHeight();
14242         if(!this.buttons){
14243             /**
14244              * Array of all the buttons that have been added to this dialog via addButton
14245              * @type Array
14246              */
14247             this.buttons = [];
14248         }
14249         this.buttons.push(btn);
14250         return btn;
14251     },
14252
14253     /**
14254      * Sets the default button to be focused when the dialog is displayed.
14255      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14256      * @return {Roo.BasicDialog} this
14257      */
14258     setDefaultButton : function(btn){
14259         this.defaultButton = btn;
14260         return this;
14261     },
14262
14263     // private
14264     getHeaderFooterHeight : function(safe){
14265         var height = 0;
14266         if(this.header){
14267            height += this.header.getHeight();
14268         }
14269         if(this.footer){
14270            var fm = this.footer.getMargins();
14271             height += (this.footer.getHeight()+fm.top+fm.bottom);
14272         }
14273         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14274         height += this.centerBg.getPadding("tb");
14275         return height;
14276     },
14277
14278     // private
14279     syncBodyHeight : function()
14280     {
14281         var bd = this.body, // the text
14282             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14283             bw = this.bwrap;
14284         var height = this.size.height - this.getHeaderFooterHeight(false);
14285         bd.setHeight(height-bd.getMargins("tb"));
14286         var hh = this.header.getHeight();
14287         var h = this.size.height-hh;
14288         cb.setHeight(h);
14289         
14290         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14291         bw.setHeight(h-cb.getPadding("tb"));
14292         
14293         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14294         bd.setWidth(bw.getWidth(true));
14295         if(this.tabs){
14296             this.tabs.syncHeight();
14297             if(Roo.isIE){
14298                 this.tabs.el.repaint();
14299             }
14300         }
14301     },
14302
14303     /**
14304      * Restores the previous state of the dialog if Roo.state is configured.
14305      * @return {Roo.BasicDialog} this
14306      */
14307     restoreState : function(){
14308         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14309         if(box && box.width){
14310             this.xy = [box.x, box.y];
14311             this.resizeTo(box.width, box.height);
14312         }
14313         return this;
14314     },
14315
14316     // private
14317     beforeShow : function(){
14318         this.expand();
14319         if(this.fixedcenter){
14320             this.xy = this.el.getCenterXY(true);
14321         }
14322         if(this.modal){
14323             Roo.get(document.body).addClass("x-body-masked");
14324             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14325             this.mask.show();
14326         }
14327         this.constrainXY();
14328     },
14329
14330     // private
14331     animShow : function(){
14332         var b = Roo.get(this.animateTarget).getBox();
14333         this.proxy.setSize(b.width, b.height);
14334         this.proxy.setLocation(b.x, b.y);
14335         this.proxy.show();
14336         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14337                     true, .35, this.showEl.createDelegate(this));
14338     },
14339
14340     /**
14341      * Shows the dialog.
14342      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14343      * @return {Roo.BasicDialog} this
14344      */
14345     show : function(animateTarget){
14346         if (this.fireEvent("beforeshow", this) === false){
14347             return;
14348         }
14349         if(this.syncHeightBeforeShow){
14350             this.syncBodyHeight();
14351         }else if(this.firstShow){
14352             this.firstShow = false;
14353             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14354         }
14355         this.animateTarget = animateTarget || this.animateTarget;
14356         if(!this.el.isVisible()){
14357             this.beforeShow();
14358             if(this.animateTarget && Roo.get(this.animateTarget)){
14359                 this.animShow();
14360             }else{
14361                 this.showEl();
14362             }
14363         }
14364         return this;
14365     },
14366
14367     // private
14368     showEl : function(){
14369         this.proxy.hide();
14370         this.el.setXY(this.xy);
14371         this.el.show();
14372         this.adjustAssets(true);
14373         this.toFront();
14374         this.focus();
14375         // IE peekaboo bug - fix found by Dave Fenwick
14376         if(Roo.isIE){
14377             this.el.repaint();
14378         }
14379         this.fireEvent("show", this);
14380     },
14381
14382     /**
14383      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14384      * dialog itself will receive focus.
14385      */
14386     focus : function(){
14387         if(this.defaultButton){
14388             this.defaultButton.focus();
14389         }else{
14390             this.focusEl.focus();
14391         }
14392     },
14393
14394     // private
14395     constrainXY : function(){
14396         if(this.constraintoviewport !== false){
14397             if(!this.viewSize){
14398                 if(this.container){
14399                     var s = this.container.getSize();
14400                     this.viewSize = [s.width, s.height];
14401                 }else{
14402                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14403                 }
14404             }
14405             var s = Roo.get(this.container||document).getScroll();
14406
14407             var x = this.xy[0], y = this.xy[1];
14408             var w = this.size.width, h = this.size.height;
14409             var vw = this.viewSize[0], vh = this.viewSize[1];
14410             // only move it if it needs it
14411             var moved = false;
14412             // first validate right/bottom
14413             if(x + w > vw+s.left){
14414                 x = vw - w;
14415                 moved = true;
14416             }
14417             if(y + h > vh+s.top){
14418                 y = vh - h;
14419                 moved = true;
14420             }
14421             // then make sure top/left isn't negative
14422             if(x < s.left){
14423                 x = s.left;
14424                 moved = true;
14425             }
14426             if(y < s.top){
14427                 y = s.top;
14428                 moved = true;
14429             }
14430             if(moved){
14431                 // cache xy
14432                 this.xy = [x, y];
14433                 if(this.isVisible()){
14434                     this.el.setLocation(x, y);
14435                     this.adjustAssets();
14436                 }
14437             }
14438         }
14439     },
14440
14441     // private
14442     onDrag : function(){
14443         if(!this.proxyDrag){
14444             this.xy = this.el.getXY();
14445             this.adjustAssets();
14446         }
14447     },
14448
14449     // private
14450     adjustAssets : function(doShow){
14451         var x = this.xy[0], y = this.xy[1];
14452         var w = this.size.width, h = this.size.height;
14453         if(doShow === true){
14454             if(this.shadow){
14455                 this.shadow.show(this.el);
14456             }
14457             if(this.shim){
14458                 this.shim.show();
14459             }
14460         }
14461         if(this.shadow && this.shadow.isVisible()){
14462             this.shadow.show(this.el);
14463         }
14464         if(this.shim && this.shim.isVisible()){
14465             this.shim.setBounds(x, y, w, h);
14466         }
14467     },
14468
14469     // private
14470     adjustViewport : function(w, h){
14471         if(!w || !h){
14472             w = Roo.lib.Dom.getViewWidth();
14473             h = Roo.lib.Dom.getViewHeight();
14474         }
14475         // cache the size
14476         this.viewSize = [w, h];
14477         if(this.modal && this.mask.isVisible()){
14478             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14479             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14480         }
14481         if(this.isVisible()){
14482             this.constrainXY();
14483         }
14484     },
14485
14486     /**
14487      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14488      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14489      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14490      */
14491     destroy : function(removeEl){
14492         if(this.isVisible()){
14493             this.animateTarget = null;
14494             this.hide();
14495         }
14496         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14497         if(this.tabs){
14498             this.tabs.destroy(removeEl);
14499         }
14500         Roo.destroy(
14501              this.shim,
14502              this.proxy,
14503              this.resizer,
14504              this.close,
14505              this.mask
14506         );
14507         if(this.dd){
14508             this.dd.unreg();
14509         }
14510         if(this.buttons){
14511            for(var i = 0, len = this.buttons.length; i < len; i++){
14512                this.buttons[i].destroy();
14513            }
14514         }
14515         this.el.removeAllListeners();
14516         if(removeEl === true){
14517             this.el.update("");
14518             this.el.remove();
14519         }
14520         Roo.DialogManager.unregister(this);
14521     },
14522
14523     // private
14524     startMove : function(){
14525         if(this.proxyDrag){
14526             this.proxy.show();
14527         }
14528         if(this.constraintoviewport !== false){
14529             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14530         }
14531     },
14532
14533     // private
14534     endMove : function(){
14535         if(!this.proxyDrag){
14536             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14537         }else{
14538             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14539             this.proxy.hide();
14540         }
14541         this.refreshSize();
14542         this.adjustAssets();
14543         this.focus();
14544         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14545     },
14546
14547     /**
14548      * Brings this dialog to the front of any other visible dialogs
14549      * @return {Roo.BasicDialog} this
14550      */
14551     toFront : function(){
14552         Roo.DialogManager.bringToFront(this);
14553         return this;
14554     },
14555
14556     /**
14557      * Sends this dialog to the back (under) of any other visible dialogs
14558      * @return {Roo.BasicDialog} this
14559      */
14560     toBack : function(){
14561         Roo.DialogManager.sendToBack(this);
14562         return this;
14563     },
14564
14565     /**
14566      * Centers this dialog in the viewport
14567      * @return {Roo.BasicDialog} this
14568      */
14569     center : function(){
14570         var xy = this.el.getCenterXY(true);
14571         this.moveTo(xy[0], xy[1]);
14572         return this;
14573     },
14574
14575     /**
14576      * Moves the dialog's top-left corner to the specified point
14577      * @param {Number} x
14578      * @param {Number} y
14579      * @return {Roo.BasicDialog} this
14580      */
14581     moveTo : function(x, y){
14582         this.xy = [x,y];
14583         if(this.isVisible()){
14584             this.el.setXY(this.xy);
14585             this.adjustAssets();
14586         }
14587         return this;
14588     },
14589
14590     /**
14591      * Aligns the dialog to the specified element
14592      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14593      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14594      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14595      * @return {Roo.BasicDialog} this
14596      */
14597     alignTo : function(element, position, offsets){
14598         this.xy = this.el.getAlignToXY(element, position, offsets);
14599         if(this.isVisible()){
14600             this.el.setXY(this.xy);
14601             this.adjustAssets();
14602         }
14603         return this;
14604     },
14605
14606     /**
14607      * Anchors an element to another element and realigns it when the window is resized.
14608      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14609      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14610      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14611      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14612      * is a number, it is used as the buffer delay (defaults to 50ms).
14613      * @return {Roo.BasicDialog} this
14614      */
14615     anchorTo : function(el, alignment, offsets, monitorScroll){
14616         var action = function(){
14617             this.alignTo(el, alignment, offsets);
14618         };
14619         Roo.EventManager.onWindowResize(action, this);
14620         var tm = typeof monitorScroll;
14621         if(tm != 'undefined'){
14622             Roo.EventManager.on(window, 'scroll', action, this,
14623                 {buffer: tm == 'number' ? monitorScroll : 50});
14624         }
14625         action.call(this);
14626         return this;
14627     },
14628
14629     /**
14630      * Returns true if the dialog is visible
14631      * @return {Boolean}
14632      */
14633     isVisible : function(){
14634         return this.el.isVisible();
14635     },
14636
14637     // private
14638     animHide : function(callback){
14639         var b = Roo.get(this.animateTarget).getBox();
14640         this.proxy.show();
14641         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14642         this.el.hide();
14643         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14644                     this.hideEl.createDelegate(this, [callback]));
14645     },
14646
14647     /**
14648      * Hides the dialog.
14649      * @param {Function} callback (optional) Function to call when the dialog is hidden
14650      * @return {Roo.BasicDialog} this
14651      */
14652     hide : function(callback){
14653         if (this.fireEvent("beforehide", this) === false){
14654             return;
14655         }
14656         if(this.shadow){
14657             this.shadow.hide();
14658         }
14659         if(this.shim) {
14660           this.shim.hide();
14661         }
14662         // sometimes animateTarget seems to get set.. causing problems...
14663         // this just double checks..
14664         if(this.animateTarget && Roo.get(this.animateTarget)) {
14665            this.animHide(callback);
14666         }else{
14667             this.el.hide();
14668             this.hideEl(callback);
14669         }
14670         return this;
14671     },
14672
14673     // private
14674     hideEl : function(callback){
14675         this.proxy.hide();
14676         if(this.modal){
14677             this.mask.hide();
14678             Roo.get(document.body).removeClass("x-body-masked");
14679         }
14680         this.fireEvent("hide", this);
14681         if(typeof callback == "function"){
14682             callback();
14683         }
14684     },
14685
14686     // private
14687     hideAction : function(){
14688         this.setLeft("-10000px");
14689         this.setTop("-10000px");
14690         this.setStyle("visibility", "hidden");
14691     },
14692
14693     // private
14694     refreshSize : function(){
14695         this.size = this.el.getSize();
14696         this.xy = this.el.getXY();
14697         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14698     },
14699
14700     // private
14701     // z-index is managed by the DialogManager and may be overwritten at any time
14702     setZIndex : function(index){
14703         if(this.modal){
14704             this.mask.setStyle("z-index", index);
14705         }
14706         if(this.shim){
14707             this.shim.setStyle("z-index", ++index);
14708         }
14709         if(this.shadow){
14710             this.shadow.setZIndex(++index);
14711         }
14712         this.el.setStyle("z-index", ++index);
14713         if(this.proxy){
14714             this.proxy.setStyle("z-index", ++index);
14715         }
14716         if(this.resizer){
14717             this.resizer.proxy.setStyle("z-index", ++index);
14718         }
14719
14720         this.lastZIndex = index;
14721     },
14722
14723     /**
14724      * Returns the element for this dialog
14725      * @return {Roo.Element} The underlying dialog Element
14726      */
14727     getEl : function(){
14728         return this.el;
14729     }
14730 });
14731
14732 /**
14733  * @class Roo.DialogManager
14734  * Provides global access to BasicDialogs that have been created and
14735  * support for z-indexing (layering) multiple open dialogs.
14736  */
14737 Roo.DialogManager = function(){
14738     var list = {};
14739     var accessList = [];
14740     var front = null;
14741
14742     // private
14743     var sortDialogs = function(d1, d2){
14744         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14745     };
14746
14747     // private
14748     var orderDialogs = function(){
14749         accessList.sort(sortDialogs);
14750         var seed = Roo.DialogManager.zseed;
14751         for(var i = 0, len = accessList.length; i < len; i++){
14752             var dlg = accessList[i];
14753             if(dlg){
14754                 dlg.setZIndex(seed + (i*10));
14755             }
14756         }
14757     };
14758
14759     return {
14760         /**
14761          * The starting z-index for BasicDialogs (defaults to 9000)
14762          * @type Number The z-index value
14763          */
14764         zseed : 9000,
14765
14766         // private
14767         register : function(dlg){
14768             list[dlg.id] = dlg;
14769             accessList.push(dlg);
14770         },
14771
14772         // private
14773         unregister : function(dlg){
14774             delete list[dlg.id];
14775             var i=0;
14776             var len=0;
14777             if(!accessList.indexOf){
14778                 for(  i = 0, len = accessList.length; i < len; i++){
14779                     if(accessList[i] == dlg){
14780                         accessList.splice(i, 1);
14781                         return;
14782                     }
14783                 }
14784             }else{
14785                  i = accessList.indexOf(dlg);
14786                 if(i != -1){
14787                     accessList.splice(i, 1);
14788                 }
14789             }
14790         },
14791
14792         /**
14793          * Gets a registered dialog by id
14794          * @param {String/Object} id The id of the dialog or a dialog
14795          * @return {Roo.BasicDialog} this
14796          */
14797         get : function(id){
14798             return typeof id == "object" ? id : list[id];
14799         },
14800
14801         /**
14802          * Brings the specified dialog to the front
14803          * @param {String/Object} dlg The id of the dialog or a dialog
14804          * @return {Roo.BasicDialog} this
14805          */
14806         bringToFront : function(dlg){
14807             dlg = this.get(dlg);
14808             if(dlg != front){
14809                 front = dlg;
14810                 dlg._lastAccess = new Date().getTime();
14811                 orderDialogs();
14812             }
14813             return dlg;
14814         },
14815
14816         /**
14817          * Sends the specified dialog to the back
14818          * @param {String/Object} dlg The id of the dialog or a dialog
14819          * @return {Roo.BasicDialog} this
14820          */
14821         sendToBack : function(dlg){
14822             dlg = this.get(dlg);
14823             dlg._lastAccess = -(new Date().getTime());
14824             orderDialogs();
14825             return dlg;
14826         },
14827
14828         /**
14829          * Hides all dialogs
14830          */
14831         hideAll : function(){
14832             for(var id in list){
14833                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14834                     list[id].hide();
14835                 }
14836             }
14837         }
14838     };
14839 }();
14840
14841 /**
14842  * @class Roo.LayoutDialog
14843  * @extends Roo.BasicDialog
14844  * Dialog which provides adjustments for working with a layout in a Dialog.
14845  * Add your necessary layout config options to the dialog's config.<br>
14846  * Example usage (including a nested layout):
14847  * <pre><code>
14848 if(!dialog){
14849     dialog = new Roo.LayoutDialog("download-dlg", {
14850         modal: true,
14851         width:600,
14852         height:450,
14853         shadow:true,
14854         minWidth:500,
14855         minHeight:350,
14856         autoTabs:true,
14857         proxyDrag:true,
14858         // layout config merges with the dialog config
14859         center:{
14860             tabPosition: "top",
14861             alwaysShowTabs: true
14862         }
14863     });
14864     dialog.addKeyListener(27, dialog.hide, dialog);
14865     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14866     dialog.addButton("Build It!", this.getDownload, this);
14867
14868     // we can even add nested layouts
14869     var innerLayout = new Roo.BorderLayout("dl-inner", {
14870         east: {
14871             initialSize: 200,
14872             autoScroll:true,
14873             split:true
14874         },
14875         center: {
14876             autoScroll:true
14877         }
14878     });
14879     innerLayout.beginUpdate();
14880     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14881     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14882     innerLayout.endUpdate(true);
14883
14884     var layout = dialog.getLayout();
14885     layout.beginUpdate();
14886     layout.add("center", new Roo.ContentPanel("standard-panel",
14887                         {title: "Download the Source", fitToFrame:true}));
14888     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14889                {title: "Build your own roo.js"}));
14890     layout.getRegion("center").showPanel(sp);
14891     layout.endUpdate();
14892 }
14893 </code></pre>
14894     * @constructor
14895     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14896     * @param {Object} config configuration options
14897   */
14898 Roo.LayoutDialog = function(el, cfg){
14899     
14900     var config=  cfg;
14901     if (typeof(cfg) == 'undefined') {
14902         config = Roo.apply({}, el);
14903         // not sure why we use documentElement here.. - it should always be body.
14904         // IE7 borks horribly if we use documentElement.
14905         // webkit also does not like documentElement - it creates a body element...
14906         el = Roo.get( document.body || document.documentElement ).createChild();
14907         //config.autoCreate = true;
14908     }
14909     
14910     
14911     config.autoTabs = false;
14912     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14913     this.body.setStyle({overflow:"hidden", position:"relative"});
14914     this.layout = new Roo.BorderLayout(this.body.dom, config);
14915     this.layout.monitorWindowResize = false;
14916     this.el.addClass("x-dlg-auto-layout");
14917     // fix case when center region overwrites center function
14918     this.center = Roo.BasicDialog.prototype.center;
14919     this.on("show", this.layout.layout, this.layout, true);
14920     if (config.items) {
14921         var xitems = config.items;
14922         delete config.items;
14923         Roo.each(xitems, this.addxtype, this);
14924     }
14925     
14926     
14927 };
14928 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14929     /**
14930      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14931      * @deprecated
14932      */
14933     endUpdate : function(){
14934         this.layout.endUpdate();
14935     },
14936
14937     /**
14938      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14939      *  @deprecated
14940      */
14941     beginUpdate : function(){
14942         this.layout.beginUpdate();
14943     },
14944
14945     /**
14946      * Get the BorderLayout for this dialog
14947      * @return {Roo.BorderLayout}
14948      */
14949     getLayout : function(){
14950         return this.layout;
14951     },
14952
14953     showEl : function(){
14954         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14955         if(Roo.isIE7){
14956             this.layout.layout();
14957         }
14958     },
14959
14960     // private
14961     // Use the syncHeightBeforeShow config option to control this automatically
14962     syncBodyHeight : function(){
14963         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14964         if(this.layout){this.layout.layout();}
14965     },
14966     
14967       /**
14968      * Add an xtype element (actually adds to the layout.)
14969      * @return {Object} xdata xtype object data.
14970      */
14971     
14972     addxtype : function(c) {
14973         return this.layout.addxtype(c);
14974     }
14975 });/*
14976  * Based on:
14977  * Ext JS Library 1.1.1
14978  * Copyright(c) 2006-2007, Ext JS, LLC.
14979  *
14980  * Originally Released Under LGPL - original licence link has changed is not relivant.
14981  *
14982  * Fork - LGPL
14983  * <script type="text/javascript">
14984  */
14985  
14986 /**
14987  * @class Roo.MessageBox
14988  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14989  * Example usage:
14990  *<pre><code>
14991 // Basic alert:
14992 Roo.Msg.alert('Status', 'Changes saved successfully.');
14993
14994 // Prompt for user data:
14995 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14996     if (btn == 'ok'){
14997         // process text value...
14998     }
14999 });
15000
15001 // Show a dialog using config options:
15002 Roo.Msg.show({
15003    title:'Save Changes?',
15004    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15005    buttons: Roo.Msg.YESNOCANCEL,
15006    fn: processResult,
15007    animEl: 'elId'
15008 });
15009 </code></pre>
15010  * @singleton
15011  */
15012 Roo.MessageBox = function(){
15013     var dlg, opt, mask, waitTimer;
15014     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15015     var buttons, activeTextEl, bwidth;
15016
15017     // private
15018     var handleButton = function(button){
15019         dlg.hide();
15020         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15021     };
15022
15023     // private
15024     var handleHide = function(){
15025         if(opt && opt.cls){
15026             dlg.el.removeClass(opt.cls);
15027         }
15028         if(waitTimer){
15029             Roo.TaskMgr.stop(waitTimer);
15030             waitTimer = null;
15031         }
15032     };
15033
15034     // private
15035     var updateButtons = function(b){
15036         var width = 0;
15037         if(!b){
15038             buttons["ok"].hide();
15039             buttons["cancel"].hide();
15040             buttons["yes"].hide();
15041             buttons["no"].hide();
15042             dlg.footer.dom.style.display = 'none';
15043             return width;
15044         }
15045         dlg.footer.dom.style.display = '';
15046         for(var k in buttons){
15047             if(typeof buttons[k] != "function"){
15048                 if(b[k]){
15049                     buttons[k].show();
15050                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15051                     width += buttons[k].el.getWidth()+15;
15052                 }else{
15053                     buttons[k].hide();
15054                 }
15055             }
15056         }
15057         return width;
15058     };
15059
15060     // private
15061     var handleEsc = function(d, k, e){
15062         if(opt && opt.closable !== false){
15063             dlg.hide();
15064         }
15065         if(e){
15066             e.stopEvent();
15067         }
15068     };
15069
15070     return {
15071         /**
15072          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15073          * @return {Roo.BasicDialog} The BasicDialog element
15074          */
15075         getDialog : function(){
15076            if(!dlg){
15077                 dlg = new Roo.BasicDialog("x-msg-box", {
15078                     autoCreate : true,
15079                     shadow: true,
15080                     draggable: true,
15081                     resizable:false,
15082                     constraintoviewport:false,
15083                     fixedcenter:true,
15084                     collapsible : false,
15085                     shim:true,
15086                     modal: true,
15087                     width:400, height:100,
15088                     buttonAlign:"center",
15089                     closeClick : function(){
15090                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15091                             handleButton("no");
15092                         }else{
15093                             handleButton("cancel");
15094                         }
15095                     }
15096                 });
15097                 dlg.on("hide", handleHide);
15098                 mask = dlg.mask;
15099                 dlg.addKeyListener(27, handleEsc);
15100                 buttons = {};
15101                 var bt = this.buttonText;
15102                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15103                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15104                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15105                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15106                 bodyEl = dlg.body.createChild({
15107
15108                     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>'
15109                 });
15110                 msgEl = bodyEl.dom.firstChild;
15111                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15112                 textboxEl.enableDisplayMode();
15113                 textboxEl.addKeyListener([10,13], function(){
15114                     if(dlg.isVisible() && opt && opt.buttons){
15115                         if(opt.buttons.ok){
15116                             handleButton("ok");
15117                         }else if(opt.buttons.yes){
15118                             handleButton("yes");
15119                         }
15120                     }
15121                 });
15122                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15123                 textareaEl.enableDisplayMode();
15124                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15125                 progressEl.enableDisplayMode();
15126                 var pf = progressEl.dom.firstChild;
15127                 if (pf) {
15128                     pp = Roo.get(pf.firstChild);
15129                     pp.setHeight(pf.offsetHeight);
15130                 }
15131                 
15132             }
15133             return dlg;
15134         },
15135
15136         /**
15137          * Updates the message box body text
15138          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15139          * the XHTML-compliant non-breaking space character '&amp;#160;')
15140          * @return {Roo.MessageBox} This message box
15141          */
15142         updateText : function(text){
15143             if(!dlg.isVisible() && !opt.width){
15144                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15145             }
15146             msgEl.innerHTML = text || '&#160;';
15147       
15148             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15149             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15150             var w = Math.max(
15151                     Math.min(opt.width || cw , this.maxWidth), 
15152                     Math.max(opt.minWidth || this.minWidth, bwidth)
15153             );
15154             if(opt.prompt){
15155                 activeTextEl.setWidth(w);
15156             }
15157             if(dlg.isVisible()){
15158                 dlg.fixedcenter = false;
15159             }
15160             // to big, make it scroll. = But as usual stupid IE does not support
15161             // !important..
15162             
15163             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15164                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15165                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15166             } else {
15167                 bodyEl.dom.style.height = '';
15168                 bodyEl.dom.style.overflowY = '';
15169             }
15170             if (cw > w) {
15171                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15172             } else {
15173                 bodyEl.dom.style.overflowX = '';
15174             }
15175             
15176             dlg.setContentSize(w, bodyEl.getHeight());
15177             if(dlg.isVisible()){
15178                 dlg.fixedcenter = true;
15179             }
15180             return this;
15181         },
15182
15183         /**
15184          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15185          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15186          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15187          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15188          * @return {Roo.MessageBox} This message box
15189          */
15190         updateProgress : function(value, text){
15191             if(text){
15192                 this.updateText(text);
15193             }
15194             if (pp) { // weird bug on my firefox - for some reason this is not defined
15195                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15196             }
15197             return this;
15198         },        
15199
15200         /**
15201          * Returns true if the message box is currently displayed
15202          * @return {Boolean} True if the message box is visible, else false
15203          */
15204         isVisible : function(){
15205             return dlg && dlg.isVisible();  
15206         },
15207
15208         /**
15209          * Hides the message box if it is displayed
15210          */
15211         hide : function(){
15212             if(this.isVisible()){
15213                 dlg.hide();
15214             }  
15215         },
15216
15217         /**
15218          * Displays a new message box, or reinitializes an existing message box, based on the config options
15219          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15220          * The following config object properties are supported:
15221          * <pre>
15222 Property    Type             Description
15223 ----------  ---------------  ------------------------------------------------------------------------------------
15224 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15225                                    closes (defaults to undefined)
15226 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15227                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15228 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15229                                    progress and wait dialogs will ignore this property and always hide the
15230                                    close button as they can only be closed programmatically.
15231 cls               String           A custom CSS class to apply to the message box element
15232 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15233                                    displayed (defaults to 75)
15234 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15235                                    function will be btn (the name of the button that was clicked, if applicable,
15236                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15237                                    Progress and wait dialogs will ignore this option since they do not respond to
15238                                    user actions and can only be closed programmatically, so any required function
15239                                    should be called by the same code after it closes the dialog.
15240 icon              String           A CSS class that provides a background image to be used as an icon for
15241                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15242 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15243 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15244 modal             Boolean          False to allow user interaction with the page while the message box is
15245                                    displayed (defaults to true)
15246 msg               String           A string that will replace the existing message box body text (defaults
15247                                    to the XHTML-compliant non-breaking space character '&#160;')
15248 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15249 progress          Boolean          True to display a progress bar (defaults to false)
15250 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15251 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15252 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15253 title             String           The title text
15254 value             String           The string value to set into the active textbox element if displayed
15255 wait              Boolean          True to display a progress bar (defaults to false)
15256 width             Number           The width of the dialog in pixels
15257 </pre>
15258          *
15259          * Example usage:
15260          * <pre><code>
15261 Roo.Msg.show({
15262    title: 'Address',
15263    msg: 'Please enter your address:',
15264    width: 300,
15265    buttons: Roo.MessageBox.OKCANCEL,
15266    multiline: true,
15267    fn: saveAddress,
15268    animEl: 'addAddressBtn'
15269 });
15270 </code></pre>
15271          * @param {Object} config Configuration options
15272          * @return {Roo.MessageBox} This message box
15273          */
15274         show : function(options)
15275         {
15276             
15277             // this causes nightmares if you show one dialog after another
15278             // especially on callbacks..
15279              
15280             if(this.isVisible()){
15281                 
15282                 this.hide();
15283                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15284                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15285                 Roo.log("New Dialog Message:" +  options.msg )
15286                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15287                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15288                 
15289             }
15290             var d = this.getDialog();
15291             opt = options;
15292             d.setTitle(opt.title || "&#160;");
15293             d.close.setDisplayed(opt.closable !== false);
15294             activeTextEl = textboxEl;
15295             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15296             if(opt.prompt){
15297                 if(opt.multiline){
15298                     textboxEl.hide();
15299                     textareaEl.show();
15300                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15301                         opt.multiline : this.defaultTextHeight);
15302                     activeTextEl = textareaEl;
15303                 }else{
15304                     textboxEl.show();
15305                     textareaEl.hide();
15306                 }
15307             }else{
15308                 textboxEl.hide();
15309                 textareaEl.hide();
15310             }
15311             progressEl.setDisplayed(opt.progress === true);
15312             this.updateProgress(0);
15313             activeTextEl.dom.value = opt.value || "";
15314             if(opt.prompt){
15315                 dlg.setDefaultButton(activeTextEl);
15316             }else{
15317                 var bs = opt.buttons;
15318                 var db = null;
15319                 if(bs && bs.ok){
15320                     db = buttons["ok"];
15321                 }else if(bs && bs.yes){
15322                     db = buttons["yes"];
15323                 }
15324                 dlg.setDefaultButton(db);
15325             }
15326             bwidth = updateButtons(opt.buttons);
15327             this.updateText(opt.msg);
15328             if(opt.cls){
15329                 d.el.addClass(opt.cls);
15330             }
15331             d.proxyDrag = opt.proxyDrag === true;
15332             d.modal = opt.modal !== false;
15333             d.mask = opt.modal !== false ? mask : false;
15334             if(!d.isVisible()){
15335                 // force it to the end of the z-index stack so it gets a cursor in FF
15336                 document.body.appendChild(dlg.el.dom);
15337                 d.animateTarget = null;
15338                 d.show(options.animEl);
15339             }
15340             return this;
15341         },
15342
15343         /**
15344          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15345          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15346          * and closing the message box when the process is complete.
15347          * @param {String} title The title bar text
15348          * @param {String} msg The message box body text
15349          * @return {Roo.MessageBox} This message box
15350          */
15351         progress : function(title, msg){
15352             this.show({
15353                 title : title,
15354                 msg : msg,
15355                 buttons: false,
15356                 progress:true,
15357                 closable:false,
15358                 minWidth: this.minProgressWidth,
15359                 modal : true
15360             });
15361             return this;
15362         },
15363
15364         /**
15365          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15366          * If a callback function is passed it will be called after the user clicks the button, and the
15367          * id of the button that was clicked will be passed as the only parameter to the callback
15368          * (could also be the top-right close button).
15369          * @param {String} title The title bar text
15370          * @param {String} msg The message box body text
15371          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15372          * @param {Object} scope (optional) The scope of the callback function
15373          * @return {Roo.MessageBox} This message box
15374          */
15375         alert : function(title, msg, fn, scope){
15376             this.show({
15377                 title : title,
15378                 msg : msg,
15379                 buttons: this.OK,
15380                 fn: fn,
15381                 scope : scope,
15382                 modal : true
15383             });
15384             return this;
15385         },
15386
15387         /**
15388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15390          * You are responsible for closing the message box when the process is complete.
15391          * @param {String} msg The message box body text
15392          * @param {String} title (optional) The title bar text
15393          * @return {Roo.MessageBox} This message box
15394          */
15395         wait : function(msg, title){
15396             this.show({
15397                 title : title,
15398                 msg : msg,
15399                 buttons: false,
15400                 closable:false,
15401                 progress:true,
15402                 modal:true,
15403                 width:300,
15404                 wait:true
15405             });
15406             waitTimer = Roo.TaskMgr.start({
15407                 run: function(i){
15408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15409                 },
15410                 interval: 1000
15411             });
15412             return this;
15413         },
15414
15415         /**
15416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15419          * @param {String} title The title bar text
15420          * @param {String} msg The message box body text
15421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15422          * @param {Object} scope (optional) The scope of the callback function
15423          * @return {Roo.MessageBox} This message box
15424          */
15425         confirm : function(title, msg, fn, scope){
15426             this.show({
15427                 title : title,
15428                 msg : msg,
15429                 buttons: this.YESNO,
15430                 fn: fn,
15431                 scope : scope,
15432                 modal : true
15433             });
15434             return this;
15435         },
15436
15437         /**
15438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15441          * (could also be the top-right close button) and the text that was entered will be passed as the two
15442          * parameters to the callback.
15443          * @param {String} title The title bar text
15444          * @param {String} msg The message box body text
15445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15446          * @param {Object} scope (optional) The scope of the callback function
15447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15449          * @return {Roo.MessageBox} This message box
15450          */
15451         prompt : function(title, msg, fn, scope, multiline){
15452             this.show({
15453                 title : title,
15454                 msg : msg,
15455                 buttons: this.OKCANCEL,
15456                 fn: fn,
15457                 minWidth:250,
15458                 scope : scope,
15459                 prompt:true,
15460                 multiline: multiline,
15461                 modal : true
15462             });
15463             return this;
15464         },
15465
15466         /**
15467          * Button config that displays a single OK button
15468          * @type Object
15469          */
15470         OK : {ok:true},
15471         /**
15472          * Button config that displays Yes and No buttons
15473          * @type Object
15474          */
15475         YESNO : {yes:true, no:true},
15476         /**
15477          * Button config that displays OK and Cancel buttons
15478          * @type Object
15479          */
15480         OKCANCEL : {ok:true, cancel:true},
15481         /**
15482          * Button config that displays Yes, No and Cancel buttons
15483          * @type Object
15484          */
15485         YESNOCANCEL : {yes:true, no:true, cancel:true},
15486
15487         /**
15488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15489          * @type Number
15490          */
15491         defaultTextHeight : 75,
15492         /**
15493          * The maximum width in pixels of the message box (defaults to 600)
15494          * @type Number
15495          */
15496         maxWidth : 600,
15497         /**
15498          * The minimum width in pixels of the message box (defaults to 100)
15499          * @type Number
15500          */
15501         minWidth : 100,
15502         /**
15503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15505          * @type Number
15506          */
15507         minProgressWidth : 250,
15508         /**
15509          * An object containing the default button text strings that can be overriden for localized language support.
15510          * Supported properties are: ok, cancel, yes and no.
15511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15512          * @type Object
15513          */
15514         buttonText : {
15515             ok : "OK",
15516             cancel : "Cancel",
15517             yes : "Yes",
15518             no : "No"
15519         }
15520     };
15521 }();
15522
15523 /**
15524  * Shorthand for {@link Roo.MessageBox}
15525  */
15526 Roo.Msg = Roo.MessageBox;/*
15527  * Based on:
15528  * Ext JS Library 1.1.1
15529  * Copyright(c) 2006-2007, Ext JS, LLC.
15530  *
15531  * Originally Released Under LGPL - original licence link has changed is not relivant.
15532  *
15533  * Fork - LGPL
15534  * <script type="text/javascript">
15535  */
15536 /**
15537  * @class Roo.QuickTips
15538  * Provides attractive and customizable tooltips for any element.
15539  * @singleton
15540  */
15541 Roo.QuickTips = function(){
15542     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15543     var ce, bd, xy, dd;
15544     var visible = false, disabled = true, inited = false;
15545     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15546     
15547     var onOver = function(e){
15548         if(disabled){
15549             return;
15550         }
15551         var t = e.getTarget();
15552         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15553             return;
15554         }
15555         if(ce && t == ce.el){
15556             clearTimeout(hideProc);
15557             return;
15558         }
15559         if(t && tagEls[t.id]){
15560             tagEls[t.id].el = t;
15561             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15562             return;
15563         }
15564         var ttp, et = Roo.fly(t);
15565         var ns = cfg.namespace;
15566         if(tm.interceptTitles && t.title){
15567             ttp = t.title;
15568             t.qtip = ttp;
15569             t.removeAttribute("title");
15570             e.preventDefault();
15571         }else{
15572             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15573         }
15574         if(ttp){
15575             showProc = show.defer(tm.showDelay, tm, [{
15576                 el: t, 
15577                 text: ttp, 
15578                 width: et.getAttributeNS(ns, cfg.width),
15579                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15580                 title: et.getAttributeNS(ns, cfg.title),
15581                     cls: et.getAttributeNS(ns, cfg.cls)
15582             }]);
15583         }
15584     };
15585     
15586     var onOut = function(e){
15587         clearTimeout(showProc);
15588         var t = e.getTarget();
15589         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15590             hideProc = setTimeout(hide, tm.hideDelay);
15591         }
15592     };
15593     
15594     var onMove = function(e){
15595         if(disabled){
15596             return;
15597         }
15598         xy = e.getXY();
15599         xy[1] += 18;
15600         if(tm.trackMouse && ce){
15601             el.setXY(xy);
15602         }
15603     };
15604     
15605     var onDown = function(e){
15606         clearTimeout(showProc);
15607         clearTimeout(hideProc);
15608         if(!e.within(el)){
15609             if(tm.hideOnClick){
15610                 hide();
15611                 tm.disable();
15612                 tm.enable.defer(100, tm);
15613             }
15614         }
15615     };
15616     
15617     var getPad = function(){
15618         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15619     };
15620
15621     var show = function(o){
15622         if(disabled){
15623             return;
15624         }
15625         clearTimeout(dismissProc);
15626         ce = o;
15627         if(removeCls){ // in case manually hidden
15628             el.removeClass(removeCls);
15629             removeCls = null;
15630         }
15631         if(ce.cls){
15632             el.addClass(ce.cls);
15633             removeCls = ce.cls;
15634         }
15635         if(ce.title){
15636             tipTitle.update(ce.title);
15637             tipTitle.show();
15638         }else{
15639             tipTitle.update('');
15640             tipTitle.hide();
15641         }
15642         el.dom.style.width  = tm.maxWidth+'px';
15643         //tipBody.dom.style.width = '';
15644         tipBodyText.update(o.text);
15645         var p = getPad(), w = ce.width;
15646         if(!w){
15647             var td = tipBodyText.dom;
15648             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15649             if(aw > tm.maxWidth){
15650                 w = tm.maxWidth;
15651             }else if(aw < tm.minWidth){
15652                 w = tm.minWidth;
15653             }else{
15654                 w = aw;
15655             }
15656         }
15657         //tipBody.setWidth(w);
15658         el.setWidth(parseInt(w, 10) + p);
15659         if(ce.autoHide === false){
15660             close.setDisplayed(true);
15661             if(dd){
15662                 dd.unlock();
15663             }
15664         }else{
15665             close.setDisplayed(false);
15666             if(dd){
15667                 dd.lock();
15668             }
15669         }
15670         if(xy){
15671             el.avoidY = xy[1]-18;
15672             el.setXY(xy);
15673         }
15674         if(tm.animate){
15675             el.setOpacity(.1);
15676             el.setStyle("visibility", "visible");
15677             el.fadeIn({callback: afterShow});
15678         }else{
15679             afterShow();
15680         }
15681     };
15682     
15683     var afterShow = function(){
15684         if(ce){
15685             el.show();
15686             esc.enable();
15687             if(tm.autoDismiss && ce.autoHide !== false){
15688                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15689             }
15690         }
15691     };
15692     
15693     var hide = function(noanim){
15694         clearTimeout(dismissProc);
15695         clearTimeout(hideProc);
15696         ce = null;
15697         if(el.isVisible()){
15698             esc.disable();
15699             if(noanim !== true && tm.animate){
15700                 el.fadeOut({callback: afterHide});
15701             }else{
15702                 afterHide();
15703             } 
15704         }
15705     };
15706     
15707     var afterHide = function(){
15708         el.hide();
15709         if(removeCls){
15710             el.removeClass(removeCls);
15711             removeCls = null;
15712         }
15713     };
15714     
15715     return {
15716         /**
15717         * @cfg {Number} minWidth
15718         * The minimum width of the quick tip (defaults to 40)
15719         */
15720        minWidth : 40,
15721         /**
15722         * @cfg {Number} maxWidth
15723         * The maximum width of the quick tip (defaults to 300)
15724         */
15725        maxWidth : 300,
15726         /**
15727         * @cfg {Boolean} interceptTitles
15728         * True to automatically use the element's DOM title value if available (defaults to false)
15729         */
15730        interceptTitles : false,
15731         /**
15732         * @cfg {Boolean} trackMouse
15733         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15734         */
15735        trackMouse : false,
15736         /**
15737         * @cfg {Boolean} hideOnClick
15738         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15739         */
15740        hideOnClick : true,
15741         /**
15742         * @cfg {Number} showDelay
15743         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15744         */
15745        showDelay : 500,
15746         /**
15747         * @cfg {Number} hideDelay
15748         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15749         */
15750        hideDelay : 200,
15751         /**
15752         * @cfg {Boolean} autoHide
15753         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15754         * Used in conjunction with hideDelay.
15755         */
15756        autoHide : true,
15757         /**
15758         * @cfg {Boolean}
15759         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15760         * (defaults to true).  Used in conjunction with autoDismissDelay.
15761         */
15762        autoDismiss : true,
15763         /**
15764         * @cfg {Number}
15765         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15766         */
15767        autoDismissDelay : 5000,
15768        /**
15769         * @cfg {Boolean} animate
15770         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15771         */
15772        animate : false,
15773
15774        /**
15775         * @cfg {String} title
15776         * Title text to display (defaults to '').  This can be any valid HTML markup.
15777         */
15778         title: '',
15779        /**
15780         * @cfg {String} text
15781         * Body text to display (defaults to '').  This can be any valid HTML markup.
15782         */
15783         text : '',
15784        /**
15785         * @cfg {String} cls
15786         * A CSS class to apply to the base quick tip element (defaults to '').
15787         */
15788         cls : '',
15789        /**
15790         * @cfg {Number} width
15791         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15792         * minWidth or maxWidth.
15793         */
15794         width : null,
15795
15796     /**
15797      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15798      * or display QuickTips in a page.
15799      */
15800        init : function(){
15801           tm = Roo.QuickTips;
15802           cfg = tm.tagConfig;
15803           if(!inited){
15804               if(!Roo.isReady){ // allow calling of init() before onReady
15805                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15806                   return;
15807               }
15808               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15809               el.fxDefaults = {stopFx: true};
15810               // maximum custom styling
15811               //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>');
15812               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>');              
15813               tipTitle = el.child('h3');
15814               tipTitle.enableDisplayMode("block");
15815               tipBody = el.child('div.x-tip-bd');
15816               tipBodyText = el.child('div.x-tip-bd-inner');
15817               //bdLeft = el.child('div.x-tip-bd-left');
15818               //bdRight = el.child('div.x-tip-bd-right');
15819               close = el.child('div.x-tip-close');
15820               close.enableDisplayMode("block");
15821               close.on("click", hide);
15822               var d = Roo.get(document);
15823               d.on("mousedown", onDown);
15824               d.on("mouseover", onOver);
15825               d.on("mouseout", onOut);
15826               d.on("mousemove", onMove);
15827               esc = d.addKeyListener(27, hide);
15828               esc.disable();
15829               if(Roo.dd.DD){
15830                   dd = el.initDD("default", null, {
15831                       onDrag : function(){
15832                           el.sync();  
15833                       }
15834                   });
15835                   dd.setHandleElId(tipTitle.id);
15836                   dd.lock();
15837               }
15838               inited = true;
15839           }
15840           this.enable(); 
15841        },
15842
15843     /**
15844      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15845      * are supported:
15846      * <pre>
15847 Property    Type                   Description
15848 ----------  ---------------------  ------------------------------------------------------------------------
15849 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15850      * </ul>
15851      * @param {Object} config The config object
15852      */
15853        register : function(config){
15854            var cs = config instanceof Array ? config : arguments;
15855            for(var i = 0, len = cs.length; i < len; i++) {
15856                var c = cs[i];
15857                var target = c.target;
15858                if(target){
15859                    if(target instanceof Array){
15860                        for(var j = 0, jlen = target.length; j < jlen; j++){
15861                            tagEls[target[j]] = c;
15862                        }
15863                    }else{
15864                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15865                    }
15866                }
15867            }
15868        },
15869
15870     /**
15871      * Removes this quick tip from its element and destroys it.
15872      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15873      */
15874        unregister : function(el){
15875            delete tagEls[Roo.id(el)];
15876        },
15877
15878     /**
15879      * Enable this quick tip.
15880      */
15881        enable : function(){
15882            if(inited && disabled){
15883                locks.pop();
15884                if(locks.length < 1){
15885                    disabled = false;
15886                }
15887            }
15888        },
15889
15890     /**
15891      * Disable this quick tip.
15892      */
15893        disable : function(){
15894           disabled = true;
15895           clearTimeout(showProc);
15896           clearTimeout(hideProc);
15897           clearTimeout(dismissProc);
15898           if(ce){
15899               hide(true);
15900           }
15901           locks.push(1);
15902        },
15903
15904     /**
15905      * Returns true if the quick tip is enabled, else false.
15906      */
15907        isEnabled : function(){
15908             return !disabled;
15909        },
15910
15911         // private
15912        tagConfig : {
15913            namespace : "ext",
15914            attribute : "qtip",
15915            width : "width",
15916            target : "target",
15917            title : "qtitle",
15918            hide : "hide",
15919            cls : "qclass"
15920        }
15921    };
15922 }();
15923
15924 // backwards compat
15925 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15926  * Based on:
15927  * Ext JS Library 1.1.1
15928  * Copyright(c) 2006-2007, Ext JS, LLC.
15929  *
15930  * Originally Released Under LGPL - original licence link has changed is not relivant.
15931  *
15932  * Fork - LGPL
15933  * <script type="text/javascript">
15934  */
15935  
15936
15937 /**
15938  * @class Roo.tree.TreePanel
15939  * @extends Roo.data.Tree
15940
15941  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15942  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15943  * @cfg {Boolean} enableDD true to enable drag and drop
15944  * @cfg {Boolean} enableDrag true to enable just drag
15945  * @cfg {Boolean} enableDrop true to enable just drop
15946  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15947  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15948  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15949  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15950  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15951  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15952  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15953  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15954  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15955  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15956  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15957  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15958  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15959  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15960  * @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>
15961  * @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>
15962  * 
15963  * @constructor
15964  * @param {String/HTMLElement/Element} el The container element
15965  * @param {Object} config
15966  */
15967 Roo.tree.TreePanel = function(el, config){
15968     var root = false;
15969     var loader = false;
15970     if (config.root) {
15971         root = config.root;
15972         delete config.root;
15973     }
15974     if (config.loader) {
15975         loader = config.loader;
15976         delete config.loader;
15977     }
15978     
15979     Roo.apply(this, config);
15980     Roo.tree.TreePanel.superclass.constructor.call(this);
15981     this.el = Roo.get(el);
15982     this.el.addClass('x-tree');
15983     //console.log(root);
15984     if (root) {
15985         this.setRootNode( Roo.factory(root, Roo.tree));
15986     }
15987     if (loader) {
15988         this.loader = Roo.factory(loader, Roo.tree);
15989     }
15990    /**
15991     * Read-only. The id of the container element becomes this TreePanel's id.
15992     */
15993     this.id = this.el.id;
15994     this.addEvents({
15995         /**
15996         * @event beforeload
15997         * Fires before a node is loaded, return false to cancel
15998         * @param {Node} node The node being loaded
15999         */
16000         "beforeload" : true,
16001         /**
16002         * @event load
16003         * Fires when a node is loaded
16004         * @param {Node} node The node that was loaded
16005         */
16006         "load" : true,
16007         /**
16008         * @event textchange
16009         * Fires when the text for a node is changed
16010         * @param {Node} node The node
16011         * @param {String} text The new text
16012         * @param {String} oldText The old text
16013         */
16014         "textchange" : true,
16015         /**
16016         * @event beforeexpand
16017         * Fires before a node is expanded, return false to cancel.
16018         * @param {Node} node The node
16019         * @param {Boolean} deep
16020         * @param {Boolean} anim
16021         */
16022         "beforeexpand" : true,
16023         /**
16024         * @event beforecollapse
16025         * Fires before a node is collapsed, return false to cancel.
16026         * @param {Node} node The node
16027         * @param {Boolean} deep
16028         * @param {Boolean} anim
16029         */
16030         "beforecollapse" : true,
16031         /**
16032         * @event expand
16033         * Fires when a node is expanded
16034         * @param {Node} node The node
16035         */
16036         "expand" : true,
16037         /**
16038         * @event disabledchange
16039         * Fires when the disabled status of a node changes
16040         * @param {Node} node The node
16041         * @param {Boolean} disabled
16042         */
16043         "disabledchange" : true,
16044         /**
16045         * @event collapse
16046         * Fires when a node is collapsed
16047         * @param {Node} node The node
16048         */
16049         "collapse" : true,
16050         /**
16051         * @event beforeclick
16052         * Fires before click processing on a node. Return false to cancel the default action.
16053         * @param {Node} node The node
16054         * @param {Roo.EventObject} e The event object
16055         */
16056         "beforeclick":true,
16057         /**
16058         * @event checkchange
16059         * Fires when a node with a checkbox's checked property changes
16060         * @param {Node} this This node
16061         * @param {Boolean} checked
16062         */
16063         "checkchange":true,
16064         /**
16065         * @event click
16066         * Fires when a node is clicked
16067         * @param {Node} node The node
16068         * @param {Roo.EventObject} e The event object
16069         */
16070         "click":true,
16071         /**
16072         * @event dblclick
16073         * Fires when a node is double clicked
16074         * @param {Node} node The node
16075         * @param {Roo.EventObject} e The event object
16076         */
16077         "dblclick":true,
16078         /**
16079         * @event contextmenu
16080         * Fires when a node is right clicked
16081         * @param {Node} node The node
16082         * @param {Roo.EventObject} e The event object
16083         */
16084         "contextmenu":true,
16085         /**
16086         * @event beforechildrenrendered
16087         * Fires right before the child nodes for a node are rendered
16088         * @param {Node} node The node
16089         */
16090         "beforechildrenrendered":true,
16091         /**
16092         * @event startdrag
16093         * Fires when a node starts being dragged
16094         * @param {Roo.tree.TreePanel} this
16095         * @param {Roo.tree.TreeNode} node
16096         * @param {event} e The raw browser event
16097         */ 
16098        "startdrag" : true,
16099        /**
16100         * @event enddrag
16101         * Fires when a drag operation is complete
16102         * @param {Roo.tree.TreePanel} this
16103         * @param {Roo.tree.TreeNode} node
16104         * @param {event} e The raw browser event
16105         */
16106        "enddrag" : true,
16107        /**
16108         * @event dragdrop
16109         * Fires when a dragged node is dropped on a valid DD target
16110         * @param {Roo.tree.TreePanel} this
16111         * @param {Roo.tree.TreeNode} node
16112         * @param {DD} dd The dd it was dropped on
16113         * @param {event} e The raw browser event
16114         */
16115        "dragdrop" : true,
16116        /**
16117         * @event beforenodedrop
16118         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16119         * passed to handlers has the following properties:<br />
16120         * <ul style="padding:5px;padding-left:16px;">
16121         * <li>tree - The TreePanel</li>
16122         * <li>target - The node being targeted for the drop</li>
16123         * <li>data - The drag data from the drag source</li>
16124         * <li>point - The point of the drop - append, above or below</li>
16125         * <li>source - The drag source</li>
16126         * <li>rawEvent - Raw mouse event</li>
16127         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16128         * to be inserted by setting them on this object.</li>
16129         * <li>cancel - Set this to true to cancel the drop.</li>
16130         * </ul>
16131         * @param {Object} dropEvent
16132         */
16133        "beforenodedrop" : true,
16134        /**
16135         * @event nodedrop
16136         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16137         * passed to handlers has the following properties:<br />
16138         * <ul style="padding:5px;padding-left:16px;">
16139         * <li>tree - The TreePanel</li>
16140         * <li>target - The node being targeted for the drop</li>
16141         * <li>data - The drag data from the drag source</li>
16142         * <li>point - The point of the drop - append, above or below</li>
16143         * <li>source - The drag source</li>
16144         * <li>rawEvent - Raw mouse event</li>
16145         * <li>dropNode - Dropped node(s).</li>
16146         * </ul>
16147         * @param {Object} dropEvent
16148         */
16149        "nodedrop" : true,
16150         /**
16151         * @event nodedragover
16152         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16153         * passed to handlers has the following properties:<br />
16154         * <ul style="padding:5px;padding-left:16px;">
16155         * <li>tree - The TreePanel</li>
16156         * <li>target - The node being targeted for the drop</li>
16157         * <li>data - The drag data from the drag source</li>
16158         * <li>point - The point of the drop - append, above or below</li>
16159         * <li>source - The drag source</li>
16160         * <li>rawEvent - Raw mouse event</li>
16161         * <li>dropNode - Drop node(s) provided by the source.</li>
16162         * <li>cancel - Set this to true to signal drop not allowed.</li>
16163         * </ul>
16164         * @param {Object} dragOverEvent
16165         */
16166        "nodedragover" : true
16167         
16168     });
16169     if(this.singleExpand){
16170        this.on("beforeexpand", this.restrictExpand, this);
16171     }
16172     if (this.editor) {
16173         this.editor.tree = this;
16174         this.editor = Roo.factory(this.editor, Roo.tree);
16175     }
16176     
16177     if (this.selModel) {
16178         this.selModel = Roo.factory(this.selModel, Roo.tree);
16179     }
16180    
16181 };
16182 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16183     rootVisible : true,
16184     animate: Roo.enableFx,
16185     lines : true,
16186     enableDD : false,
16187     hlDrop : Roo.enableFx,
16188   
16189     renderer: false,
16190     
16191     rendererTip: false,
16192     // private
16193     restrictExpand : function(node){
16194         var p = node.parentNode;
16195         if(p){
16196             if(p.expandedChild && p.expandedChild.parentNode == p){
16197                 p.expandedChild.collapse();
16198             }
16199             p.expandedChild = node;
16200         }
16201     },
16202
16203     // private override
16204     setRootNode : function(node){
16205         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16206         if(!this.rootVisible){
16207             node.ui = new Roo.tree.RootTreeNodeUI(node);
16208         }
16209         return node;
16210     },
16211
16212     /**
16213      * Returns the container element for this TreePanel
16214      */
16215     getEl : function(){
16216         return this.el;
16217     },
16218
16219     /**
16220      * Returns the default TreeLoader for this TreePanel
16221      */
16222     getLoader : function(){
16223         return this.loader;
16224     },
16225
16226     /**
16227      * Expand all nodes
16228      */
16229     expandAll : function(){
16230         this.root.expand(true);
16231     },
16232
16233     /**
16234      * Collapse all nodes
16235      */
16236     collapseAll : function(){
16237         this.root.collapse(true);
16238     },
16239
16240     /**
16241      * Returns the selection model used by this TreePanel
16242      */
16243     getSelectionModel : function(){
16244         if(!this.selModel){
16245             this.selModel = new Roo.tree.DefaultSelectionModel();
16246         }
16247         return this.selModel;
16248     },
16249
16250     /**
16251      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16252      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16253      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16254      * @return {Array}
16255      */
16256     getChecked : function(a, startNode){
16257         startNode = startNode || this.root;
16258         var r = [];
16259         var f = function(){
16260             if(this.attributes.checked){
16261                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16262             }
16263         }
16264         startNode.cascade(f);
16265         return r;
16266     },
16267
16268     /**
16269      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16270      * @param {String} path
16271      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16272      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16273      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16274      */
16275     expandPath : function(path, attr, callback){
16276         attr = attr || "id";
16277         var keys = path.split(this.pathSeparator);
16278         var curNode = this.root;
16279         if(curNode.attributes[attr] != keys[1]){ // invalid root
16280             if(callback){
16281                 callback(false, null);
16282             }
16283             return;
16284         }
16285         var index = 1;
16286         var f = function(){
16287             if(++index == keys.length){
16288                 if(callback){
16289                     callback(true, curNode);
16290                 }
16291                 return;
16292             }
16293             var c = curNode.findChild(attr, keys[index]);
16294             if(!c){
16295                 if(callback){
16296                     callback(false, curNode);
16297                 }
16298                 return;
16299             }
16300             curNode = c;
16301             c.expand(false, false, f);
16302         };
16303         curNode.expand(false, false, f);
16304     },
16305
16306     /**
16307      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16308      * @param {String} path
16309      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16310      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16311      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16312      */
16313     selectPath : function(path, attr, callback){
16314         attr = attr || "id";
16315         var keys = path.split(this.pathSeparator);
16316         var v = keys.pop();
16317         if(keys.length > 0){
16318             var f = function(success, node){
16319                 if(success && node){
16320                     var n = node.findChild(attr, v);
16321                     if(n){
16322                         n.select();
16323                         if(callback){
16324                             callback(true, n);
16325                         }
16326                     }else if(callback){
16327                         callback(false, n);
16328                     }
16329                 }else{
16330                     if(callback){
16331                         callback(false, n);
16332                     }
16333                 }
16334             };
16335             this.expandPath(keys.join(this.pathSeparator), attr, f);
16336         }else{
16337             this.root.select();
16338             if(callback){
16339                 callback(true, this.root);
16340             }
16341         }
16342     },
16343
16344     getTreeEl : function(){
16345         return this.el;
16346     },
16347
16348     /**
16349      * Trigger rendering of this TreePanel
16350      */
16351     render : function(){
16352         if (this.innerCt) {
16353             return this; // stop it rendering more than once!!
16354         }
16355         
16356         this.innerCt = this.el.createChild({tag:"ul",
16357                cls:"x-tree-root-ct " +
16358                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16359
16360         if(this.containerScroll){
16361             Roo.dd.ScrollManager.register(this.el);
16362         }
16363         if((this.enableDD || this.enableDrop) && !this.dropZone){
16364            /**
16365             * The dropZone used by this tree if drop is enabled
16366             * @type Roo.tree.TreeDropZone
16367             */
16368              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16369                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16370            });
16371         }
16372         if((this.enableDD || this.enableDrag) && !this.dragZone){
16373            /**
16374             * The dragZone used by this tree if drag is enabled
16375             * @type Roo.tree.TreeDragZone
16376             */
16377             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16378                ddGroup: this.ddGroup || "TreeDD",
16379                scroll: this.ddScroll
16380            });
16381         }
16382         this.getSelectionModel().init(this);
16383         if (!this.root) {
16384             Roo.log("ROOT not set in tree");
16385             return this;
16386         }
16387         this.root.render();
16388         if(!this.rootVisible){
16389             this.root.renderChildren();
16390         }
16391         return this;
16392     }
16393 });/*
16394  * Based on:
16395  * Ext JS Library 1.1.1
16396  * Copyright(c) 2006-2007, Ext JS, LLC.
16397  *
16398  * Originally Released Under LGPL - original licence link has changed is not relivant.
16399  *
16400  * Fork - LGPL
16401  * <script type="text/javascript">
16402  */
16403  
16404
16405 /**
16406  * @class Roo.tree.DefaultSelectionModel
16407  * @extends Roo.util.Observable
16408  * The default single selection for a TreePanel.
16409  * @param {Object} cfg Configuration
16410  */
16411 Roo.tree.DefaultSelectionModel = function(cfg){
16412    this.selNode = null;
16413    
16414    
16415    
16416    this.addEvents({
16417        /**
16418         * @event selectionchange
16419         * Fires when the selected node changes
16420         * @param {DefaultSelectionModel} this
16421         * @param {TreeNode} node the new selection
16422         */
16423        "selectionchange" : true,
16424
16425        /**
16426         * @event beforeselect
16427         * Fires before the selected node changes, return false to cancel the change
16428         * @param {DefaultSelectionModel} this
16429         * @param {TreeNode} node the new selection
16430         * @param {TreeNode} node the old selection
16431         */
16432        "beforeselect" : true
16433    });
16434    
16435     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16436 };
16437
16438 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16439     init : function(tree){
16440         this.tree = tree;
16441         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16442         tree.on("click", this.onNodeClick, this);
16443     },
16444     
16445     onNodeClick : function(node, e){
16446         if (e.ctrlKey && this.selNode == node)  {
16447             this.unselect(node);
16448             return;
16449         }
16450         this.select(node);
16451     },
16452     
16453     /**
16454      * Select a node.
16455      * @param {TreeNode} node The node to select
16456      * @return {TreeNode} The selected node
16457      */
16458     select : function(node){
16459         var last = this.selNode;
16460         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16461             if(last){
16462                 last.ui.onSelectedChange(false);
16463             }
16464             this.selNode = node;
16465             node.ui.onSelectedChange(true);
16466             this.fireEvent("selectionchange", this, node, last);
16467         }
16468         return node;
16469     },
16470     
16471     /**
16472      * Deselect a node.
16473      * @param {TreeNode} node The node to unselect
16474      */
16475     unselect : function(node){
16476         if(this.selNode == node){
16477             this.clearSelections();
16478         }    
16479     },
16480     
16481     /**
16482      * Clear all selections
16483      */
16484     clearSelections : function(){
16485         var n = this.selNode;
16486         if(n){
16487             n.ui.onSelectedChange(false);
16488             this.selNode = null;
16489             this.fireEvent("selectionchange", this, null);
16490         }
16491         return n;
16492     },
16493     
16494     /**
16495      * Get the selected node
16496      * @return {TreeNode} The selected node
16497      */
16498     getSelectedNode : function(){
16499         return this.selNode;    
16500     },
16501     
16502     /**
16503      * Returns true if the node is selected
16504      * @param {TreeNode} node The node to check
16505      * @return {Boolean}
16506      */
16507     isSelected : function(node){
16508         return this.selNode == node;  
16509     },
16510
16511     /**
16512      * Selects the node above the selected node in the tree, intelligently walking the nodes
16513      * @return TreeNode The new selection
16514      */
16515     selectPrevious : function(){
16516         var s = this.selNode || this.lastSelNode;
16517         if(!s){
16518             return null;
16519         }
16520         var ps = s.previousSibling;
16521         if(ps){
16522             if(!ps.isExpanded() || ps.childNodes.length < 1){
16523                 return this.select(ps);
16524             } else{
16525                 var lc = ps.lastChild;
16526                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16527                     lc = lc.lastChild;
16528                 }
16529                 return this.select(lc);
16530             }
16531         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16532             return this.select(s.parentNode);
16533         }
16534         return null;
16535     },
16536
16537     /**
16538      * Selects the node above the selected node in the tree, intelligently walking the nodes
16539      * @return TreeNode The new selection
16540      */
16541     selectNext : function(){
16542         var s = this.selNode || this.lastSelNode;
16543         if(!s){
16544             return null;
16545         }
16546         if(s.firstChild && s.isExpanded()){
16547              return this.select(s.firstChild);
16548          }else if(s.nextSibling){
16549              return this.select(s.nextSibling);
16550          }else if(s.parentNode){
16551             var newS = null;
16552             s.parentNode.bubble(function(){
16553                 if(this.nextSibling){
16554                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16555                     return false;
16556                 }
16557             });
16558             return newS;
16559          }
16560         return null;
16561     },
16562
16563     onKeyDown : function(e){
16564         var s = this.selNode || this.lastSelNode;
16565         // undesirable, but required
16566         var sm = this;
16567         if(!s){
16568             return;
16569         }
16570         var k = e.getKey();
16571         switch(k){
16572              case e.DOWN:
16573                  e.stopEvent();
16574                  this.selectNext();
16575              break;
16576              case e.UP:
16577                  e.stopEvent();
16578                  this.selectPrevious();
16579              break;
16580              case e.RIGHT:
16581                  e.preventDefault();
16582                  if(s.hasChildNodes()){
16583                      if(!s.isExpanded()){
16584                          s.expand();
16585                      }else if(s.firstChild){
16586                          this.select(s.firstChild, e);
16587                      }
16588                  }
16589              break;
16590              case e.LEFT:
16591                  e.preventDefault();
16592                  if(s.hasChildNodes() && s.isExpanded()){
16593                      s.collapse();
16594                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16595                      this.select(s.parentNode, e);
16596                  }
16597              break;
16598         };
16599     }
16600 });
16601
16602 /**
16603  * @class Roo.tree.MultiSelectionModel
16604  * @extends Roo.util.Observable
16605  * Multi selection for a TreePanel.
16606  * @param {Object} cfg Configuration
16607  */
16608 Roo.tree.MultiSelectionModel = function(){
16609    this.selNodes = [];
16610    this.selMap = {};
16611    this.addEvents({
16612        /**
16613         * @event selectionchange
16614         * Fires when the selected nodes change
16615         * @param {MultiSelectionModel} this
16616         * @param {Array} nodes Array of the selected nodes
16617         */
16618        "selectionchange" : true
16619    });
16620    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16621    
16622 };
16623
16624 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16625     init : function(tree){
16626         this.tree = tree;
16627         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16628         tree.on("click", this.onNodeClick, this);
16629     },
16630     
16631     onNodeClick : function(node, e){
16632         this.select(node, e, e.ctrlKey);
16633     },
16634     
16635     /**
16636      * Select a node.
16637      * @param {TreeNode} node The node to select
16638      * @param {EventObject} e (optional) An event associated with the selection
16639      * @param {Boolean} keepExisting True to retain existing selections
16640      * @return {TreeNode} The selected node
16641      */
16642     select : function(node, e, keepExisting){
16643         if(keepExisting !== true){
16644             this.clearSelections(true);
16645         }
16646         if(this.isSelected(node)){
16647             this.lastSelNode = node;
16648             return node;
16649         }
16650         this.selNodes.push(node);
16651         this.selMap[node.id] = node;
16652         this.lastSelNode = node;
16653         node.ui.onSelectedChange(true);
16654         this.fireEvent("selectionchange", this, this.selNodes);
16655         return node;
16656     },
16657     
16658     /**
16659      * Deselect a node.
16660      * @param {TreeNode} node The node to unselect
16661      */
16662     unselect : function(node){
16663         if(this.selMap[node.id]){
16664             node.ui.onSelectedChange(false);
16665             var sn = this.selNodes;
16666             var index = -1;
16667             if(sn.indexOf){
16668                 index = sn.indexOf(node);
16669             }else{
16670                 for(var i = 0, len = sn.length; i < len; i++){
16671                     if(sn[i] == node){
16672                         index = i;
16673                         break;
16674                     }
16675                 }
16676             }
16677             if(index != -1){
16678                 this.selNodes.splice(index, 1);
16679             }
16680             delete this.selMap[node.id];
16681             this.fireEvent("selectionchange", this, this.selNodes);
16682         }
16683     },
16684     
16685     /**
16686      * Clear all selections
16687      */
16688     clearSelections : function(suppressEvent){
16689         var sn = this.selNodes;
16690         if(sn.length > 0){
16691             for(var i = 0, len = sn.length; i < len; i++){
16692                 sn[i].ui.onSelectedChange(false);
16693             }
16694             this.selNodes = [];
16695             this.selMap = {};
16696             if(suppressEvent !== true){
16697                 this.fireEvent("selectionchange", this, this.selNodes);
16698             }
16699         }
16700     },
16701     
16702     /**
16703      * Returns true if the node is selected
16704      * @param {TreeNode} node The node to check
16705      * @return {Boolean}
16706      */
16707     isSelected : function(node){
16708         return this.selMap[node.id] ? true : false;  
16709     },
16710     
16711     /**
16712      * Returns an array of the selected nodes
16713      * @return {Array}
16714      */
16715     getSelectedNodes : function(){
16716         return this.selNodes;    
16717     },
16718
16719     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16720
16721     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16722
16723     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16724 });/*
16725  * Based on:
16726  * Ext JS Library 1.1.1
16727  * Copyright(c) 2006-2007, Ext JS, LLC.
16728  *
16729  * Originally Released Under LGPL - original licence link has changed is not relivant.
16730  *
16731  * Fork - LGPL
16732  * <script type="text/javascript">
16733  */
16734  
16735 /**
16736  * @class Roo.tree.TreeNode
16737  * @extends Roo.data.Node
16738  * @cfg {String} text The text for this node
16739  * @cfg {Boolean} expanded true to start the node expanded
16740  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16741  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16742  * @cfg {Boolean} disabled true to start the node disabled
16743  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16744  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16745  * @cfg {String} cls A css class to be added to the node
16746  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16747  * @cfg {String} href URL of the link used for the node (defaults to #)
16748  * @cfg {String} hrefTarget target frame for the link
16749  * @cfg {String} qtip An Ext QuickTip for the node
16750  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16751  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16752  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16753  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16754  * (defaults to undefined with no checkbox rendered)
16755  * @constructor
16756  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16757  */
16758 Roo.tree.TreeNode = function(attributes){
16759     attributes = attributes || {};
16760     if(typeof attributes == "string"){
16761         attributes = {text: attributes};
16762     }
16763     this.childrenRendered = false;
16764     this.rendered = false;
16765     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16766     this.expanded = attributes.expanded === true;
16767     this.isTarget = attributes.isTarget !== false;
16768     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16769     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16770
16771     /**
16772      * Read-only. The text for this node. To change it use setText().
16773      * @type String
16774      */
16775     this.text = attributes.text;
16776     /**
16777      * True if this node is disabled.
16778      * @type Boolean
16779      */
16780     this.disabled = attributes.disabled === true;
16781
16782     this.addEvents({
16783         /**
16784         * @event textchange
16785         * Fires when the text for this node is changed
16786         * @param {Node} this This node
16787         * @param {String} text The new text
16788         * @param {String} oldText The old text
16789         */
16790         "textchange" : true,
16791         /**
16792         * @event beforeexpand
16793         * Fires before this node is expanded, return false to cancel.
16794         * @param {Node} this This node
16795         * @param {Boolean} deep
16796         * @param {Boolean} anim
16797         */
16798         "beforeexpand" : true,
16799         /**
16800         * @event beforecollapse
16801         * Fires before this node is collapsed, return false to cancel.
16802         * @param {Node} this This node
16803         * @param {Boolean} deep
16804         * @param {Boolean} anim
16805         */
16806         "beforecollapse" : true,
16807         /**
16808         * @event expand
16809         * Fires when this node is expanded
16810         * @param {Node} this This node
16811         */
16812         "expand" : true,
16813         /**
16814         * @event disabledchange
16815         * Fires when the disabled status of this node changes
16816         * @param {Node} this This node
16817         * @param {Boolean} disabled
16818         */
16819         "disabledchange" : true,
16820         /**
16821         * @event collapse
16822         * Fires when this node is collapsed
16823         * @param {Node} this This node
16824         */
16825         "collapse" : true,
16826         /**
16827         * @event beforeclick
16828         * Fires before click processing. Return false to cancel the default action.
16829         * @param {Node} this This node
16830         * @param {Roo.EventObject} e The event object
16831         */
16832         "beforeclick":true,
16833         /**
16834         * @event checkchange
16835         * Fires when a node with a checkbox's checked property changes
16836         * @param {Node} this This node
16837         * @param {Boolean} checked
16838         */
16839         "checkchange":true,
16840         /**
16841         * @event click
16842         * Fires when this node is clicked
16843         * @param {Node} this This node
16844         * @param {Roo.EventObject} e The event object
16845         */
16846         "click":true,
16847         /**
16848         * @event dblclick
16849         * Fires when this node is double clicked
16850         * @param {Node} this This node
16851         * @param {Roo.EventObject} e The event object
16852         */
16853         "dblclick":true,
16854         /**
16855         * @event contextmenu
16856         * Fires when this node is right clicked
16857         * @param {Node} this This node
16858         * @param {Roo.EventObject} e The event object
16859         */
16860         "contextmenu":true,
16861         /**
16862         * @event beforechildrenrendered
16863         * Fires right before the child nodes for this node are rendered
16864         * @param {Node} this This node
16865         */
16866         "beforechildrenrendered":true
16867     });
16868
16869     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16870
16871     /**
16872      * Read-only. The UI for this node
16873      * @type TreeNodeUI
16874      */
16875     this.ui = new uiClass(this);
16876     
16877     // finally support items[]
16878     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16879         return;
16880     }
16881     
16882     
16883     Roo.each(this.attributes.items, function(c) {
16884         this.appendChild(Roo.factory(c,Roo.Tree));
16885     }, this);
16886     delete this.attributes.items;
16887     
16888     
16889     
16890 };
16891 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16892     preventHScroll: true,
16893     /**
16894      * Returns true if this node is expanded
16895      * @return {Boolean}
16896      */
16897     isExpanded : function(){
16898         return this.expanded;
16899     },
16900
16901     /**
16902      * Returns the UI object for this node
16903      * @return {TreeNodeUI}
16904      */
16905     getUI : function(){
16906         return this.ui;
16907     },
16908
16909     // private override
16910     setFirstChild : function(node){
16911         var of = this.firstChild;
16912         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16913         if(this.childrenRendered && of && node != of){
16914             of.renderIndent(true, true);
16915         }
16916         if(this.rendered){
16917             this.renderIndent(true, true);
16918         }
16919     },
16920
16921     // private override
16922     setLastChild : function(node){
16923         var ol = this.lastChild;
16924         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16925         if(this.childrenRendered && ol && node != ol){
16926             ol.renderIndent(true, true);
16927         }
16928         if(this.rendered){
16929             this.renderIndent(true, true);
16930         }
16931     },
16932
16933     // these methods are overridden to provide lazy rendering support
16934     // private override
16935     appendChild : function()
16936     {
16937         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16938         if(node && this.childrenRendered){
16939             node.render();
16940         }
16941         this.ui.updateExpandIcon();
16942         return node;
16943     },
16944
16945     // private override
16946     removeChild : function(node){
16947         this.ownerTree.getSelectionModel().unselect(node);
16948         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16949         // if it's been rendered remove dom node
16950         if(this.childrenRendered){
16951             node.ui.remove();
16952         }
16953         if(this.childNodes.length < 1){
16954             this.collapse(false, false);
16955         }else{
16956             this.ui.updateExpandIcon();
16957         }
16958         if(!this.firstChild) {
16959             this.childrenRendered = false;
16960         }
16961         return node;
16962     },
16963
16964     // private override
16965     insertBefore : function(node, refNode){
16966         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16967         if(newNode && refNode && this.childrenRendered){
16968             node.render();
16969         }
16970         this.ui.updateExpandIcon();
16971         return newNode;
16972     },
16973
16974     /**
16975      * Sets the text for this node
16976      * @param {String} text
16977      */
16978     setText : function(text){
16979         var oldText = this.text;
16980         this.text = text;
16981         this.attributes.text = text;
16982         if(this.rendered){ // event without subscribing
16983             this.ui.onTextChange(this, text, oldText);
16984         }
16985         this.fireEvent("textchange", this, text, oldText);
16986     },
16987
16988     /**
16989      * Triggers selection of this node
16990      */
16991     select : function(){
16992         this.getOwnerTree().getSelectionModel().select(this);
16993     },
16994
16995     /**
16996      * Triggers deselection of this node
16997      */
16998     unselect : function(){
16999         this.getOwnerTree().getSelectionModel().unselect(this);
17000     },
17001
17002     /**
17003      * Returns true if this node is selected
17004      * @return {Boolean}
17005      */
17006     isSelected : function(){
17007         return this.getOwnerTree().getSelectionModel().isSelected(this);
17008     },
17009
17010     /**
17011      * Expand this node.
17012      * @param {Boolean} deep (optional) True to expand all children as well
17013      * @param {Boolean} anim (optional) false to cancel the default animation
17014      * @param {Function} callback (optional) A callback to be called when
17015      * expanding this node completes (does not wait for deep expand to complete).
17016      * Called with 1 parameter, this node.
17017      */
17018     expand : function(deep, anim, callback){
17019         if(!this.expanded){
17020             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17021                 return;
17022             }
17023             if(!this.childrenRendered){
17024                 this.renderChildren();
17025             }
17026             this.expanded = true;
17027             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17028                 this.ui.animExpand(function(){
17029                     this.fireEvent("expand", this);
17030                     if(typeof callback == "function"){
17031                         callback(this);
17032                     }
17033                     if(deep === true){
17034                         this.expandChildNodes(true);
17035                     }
17036                 }.createDelegate(this));
17037                 return;
17038             }else{
17039                 this.ui.expand();
17040                 this.fireEvent("expand", this);
17041                 if(typeof callback == "function"){
17042                     callback(this);
17043                 }
17044             }
17045         }else{
17046            if(typeof callback == "function"){
17047                callback(this);
17048            }
17049         }
17050         if(deep === true){
17051             this.expandChildNodes(true);
17052         }
17053     },
17054
17055     isHiddenRoot : function(){
17056         return this.isRoot && !this.getOwnerTree().rootVisible;
17057     },
17058
17059     /**
17060      * Collapse this node.
17061      * @param {Boolean} deep (optional) True to collapse all children as well
17062      * @param {Boolean} anim (optional) false to cancel the default animation
17063      */
17064     collapse : function(deep, anim){
17065         if(this.expanded && !this.isHiddenRoot()){
17066             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17067                 return;
17068             }
17069             this.expanded = false;
17070             if((this.getOwnerTree().animate && anim !== false) || anim){
17071                 this.ui.animCollapse(function(){
17072                     this.fireEvent("collapse", this);
17073                     if(deep === true){
17074                         this.collapseChildNodes(true);
17075                     }
17076                 }.createDelegate(this));
17077                 return;
17078             }else{
17079                 this.ui.collapse();
17080                 this.fireEvent("collapse", this);
17081             }
17082         }
17083         if(deep === true){
17084             var cs = this.childNodes;
17085             for(var i = 0, len = cs.length; i < len; i++) {
17086                 cs[i].collapse(true, false);
17087             }
17088         }
17089     },
17090
17091     // private
17092     delayedExpand : function(delay){
17093         if(!this.expandProcId){
17094             this.expandProcId = this.expand.defer(delay, this);
17095         }
17096     },
17097
17098     // private
17099     cancelExpand : function(){
17100         if(this.expandProcId){
17101             clearTimeout(this.expandProcId);
17102         }
17103         this.expandProcId = false;
17104     },
17105
17106     /**
17107      * Toggles expanded/collapsed state of the node
17108      */
17109     toggle : function(){
17110         if(this.expanded){
17111             this.collapse();
17112         }else{
17113             this.expand();
17114         }
17115     },
17116
17117     /**
17118      * Ensures all parent nodes are expanded
17119      */
17120     ensureVisible : function(callback){
17121         var tree = this.getOwnerTree();
17122         tree.expandPath(this.parentNode.getPath(), false, function(){
17123             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17124             Roo.callback(callback);
17125         }.createDelegate(this));
17126     },
17127
17128     /**
17129      * Expand all child nodes
17130      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17131      */
17132     expandChildNodes : function(deep){
17133         var cs = this.childNodes;
17134         for(var i = 0, len = cs.length; i < len; i++) {
17135                 cs[i].expand(deep);
17136         }
17137     },
17138
17139     /**
17140      * Collapse all child nodes
17141      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17142      */
17143     collapseChildNodes : function(deep){
17144         var cs = this.childNodes;
17145         for(var i = 0, len = cs.length; i < len; i++) {
17146                 cs[i].collapse(deep);
17147         }
17148     },
17149
17150     /**
17151      * Disables this node
17152      */
17153     disable : function(){
17154         this.disabled = true;
17155         this.unselect();
17156         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17157             this.ui.onDisableChange(this, true);
17158         }
17159         this.fireEvent("disabledchange", this, true);
17160     },
17161
17162     /**
17163      * Enables this node
17164      */
17165     enable : function(){
17166         this.disabled = false;
17167         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17168             this.ui.onDisableChange(this, false);
17169         }
17170         this.fireEvent("disabledchange", this, false);
17171     },
17172
17173     // private
17174     renderChildren : function(suppressEvent){
17175         if(suppressEvent !== false){
17176             this.fireEvent("beforechildrenrendered", this);
17177         }
17178         var cs = this.childNodes;
17179         for(var i = 0, len = cs.length; i < len; i++){
17180             cs[i].render(true);
17181         }
17182         this.childrenRendered = true;
17183     },
17184
17185     // private
17186     sort : function(fn, scope){
17187         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17188         if(this.childrenRendered){
17189             var cs = this.childNodes;
17190             for(var i = 0, len = cs.length; i < len; i++){
17191                 cs[i].render(true);
17192             }
17193         }
17194     },
17195
17196     // private
17197     render : function(bulkRender){
17198         this.ui.render(bulkRender);
17199         if(!this.rendered){
17200             this.rendered = true;
17201             if(this.expanded){
17202                 this.expanded = false;
17203                 this.expand(false, false);
17204             }
17205         }
17206     },
17207
17208     // private
17209     renderIndent : function(deep, refresh){
17210         if(refresh){
17211             this.ui.childIndent = null;
17212         }
17213         this.ui.renderIndent();
17214         if(deep === true && this.childrenRendered){
17215             var cs = this.childNodes;
17216             for(var i = 0, len = cs.length; i < len; i++){
17217                 cs[i].renderIndent(true, refresh);
17218             }
17219         }
17220     }
17221 });/*
17222  * Based on:
17223  * Ext JS Library 1.1.1
17224  * Copyright(c) 2006-2007, Ext JS, LLC.
17225  *
17226  * Originally Released Under LGPL - original licence link has changed is not relivant.
17227  *
17228  * Fork - LGPL
17229  * <script type="text/javascript">
17230  */
17231  
17232 /**
17233  * @class Roo.tree.AsyncTreeNode
17234  * @extends Roo.tree.TreeNode
17235  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17236  * @constructor
17237  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17238  */
17239  Roo.tree.AsyncTreeNode = function(config){
17240     this.loaded = false;
17241     this.loading = false;
17242     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17243     /**
17244     * @event beforeload
17245     * Fires before this node is loaded, return false to cancel
17246     * @param {Node} this This node
17247     */
17248     this.addEvents({'beforeload':true, 'load': true});
17249     /**
17250     * @event load
17251     * Fires when this node is loaded
17252     * @param {Node} this This node
17253     */
17254     /**
17255      * The loader used by this node (defaults to using the tree's defined loader)
17256      * @type TreeLoader
17257      * @property loader
17258      */
17259 };
17260 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17261     expand : function(deep, anim, callback){
17262         if(this.loading){ // if an async load is already running, waiting til it's done
17263             var timer;
17264             var f = function(){
17265                 if(!this.loading){ // done loading
17266                     clearInterval(timer);
17267                     this.expand(deep, anim, callback);
17268                 }
17269             }.createDelegate(this);
17270             timer = setInterval(f, 200);
17271             return;
17272         }
17273         if(!this.loaded){
17274             if(this.fireEvent("beforeload", this) === false){
17275                 return;
17276             }
17277             this.loading = true;
17278             this.ui.beforeLoad(this);
17279             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17280             if(loader){
17281                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17282                 return;
17283             }
17284         }
17285         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17286     },
17287     
17288     /**
17289      * Returns true if this node is currently loading
17290      * @return {Boolean}
17291      */
17292     isLoading : function(){
17293         return this.loading;  
17294     },
17295     
17296     loadComplete : function(deep, anim, callback){
17297         this.loading = false;
17298         this.loaded = true;
17299         this.ui.afterLoad(this);
17300         this.fireEvent("load", this);
17301         this.expand(deep, anim, callback);
17302     },
17303     
17304     /**
17305      * Returns true if this node has been loaded
17306      * @return {Boolean}
17307      */
17308     isLoaded : function(){
17309         return this.loaded;
17310     },
17311     
17312     hasChildNodes : function(){
17313         if(!this.isLeaf() && !this.loaded){
17314             return true;
17315         }else{
17316             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17317         }
17318     },
17319
17320     /**
17321      * Trigger a reload for this node
17322      * @param {Function} callback
17323      */
17324     reload : function(callback){
17325         this.collapse(false, false);
17326         while(this.firstChild){
17327             this.removeChild(this.firstChild);
17328         }
17329         this.childrenRendered = false;
17330         this.loaded = false;
17331         if(this.isHiddenRoot()){
17332             this.expanded = false;
17333         }
17334         this.expand(false, false, callback);
17335     }
17336 });/*
17337  * Based on:
17338  * Ext JS Library 1.1.1
17339  * Copyright(c) 2006-2007, Ext JS, LLC.
17340  *
17341  * Originally Released Under LGPL - original licence link has changed is not relivant.
17342  *
17343  * Fork - LGPL
17344  * <script type="text/javascript">
17345  */
17346  
17347 /**
17348  * @class Roo.tree.TreeNodeUI
17349  * @constructor
17350  * @param {Object} node The node to render
17351  * The TreeNode UI implementation is separate from the
17352  * tree implementation. Unless you are customizing the tree UI,
17353  * you should never have to use this directly.
17354  */
17355 Roo.tree.TreeNodeUI = function(node){
17356     this.node = node;
17357     this.rendered = false;
17358     this.animating = false;
17359     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17360 };
17361
17362 Roo.tree.TreeNodeUI.prototype = {
17363     removeChild : function(node){
17364         if(this.rendered){
17365             this.ctNode.removeChild(node.ui.getEl());
17366         }
17367     },
17368
17369     beforeLoad : function(){
17370          this.addClass("x-tree-node-loading");
17371     },
17372
17373     afterLoad : function(){
17374          this.removeClass("x-tree-node-loading");
17375     },
17376
17377     onTextChange : function(node, text, oldText){
17378         if(this.rendered){
17379             this.textNode.innerHTML = text;
17380         }
17381     },
17382
17383     onDisableChange : function(node, state){
17384         this.disabled = state;
17385         if(state){
17386             this.addClass("x-tree-node-disabled");
17387         }else{
17388             this.removeClass("x-tree-node-disabled");
17389         }
17390     },
17391
17392     onSelectedChange : function(state){
17393         if(state){
17394             this.focus();
17395             this.addClass("x-tree-selected");
17396         }else{
17397             //this.blur();
17398             this.removeClass("x-tree-selected");
17399         }
17400     },
17401
17402     onMove : function(tree, node, oldParent, newParent, index, refNode){
17403         this.childIndent = null;
17404         if(this.rendered){
17405             var targetNode = newParent.ui.getContainer();
17406             if(!targetNode){//target not rendered
17407                 this.holder = document.createElement("div");
17408                 this.holder.appendChild(this.wrap);
17409                 return;
17410             }
17411             var insertBefore = refNode ? refNode.ui.getEl() : null;
17412             if(insertBefore){
17413                 targetNode.insertBefore(this.wrap, insertBefore);
17414             }else{
17415                 targetNode.appendChild(this.wrap);
17416             }
17417             this.node.renderIndent(true);
17418         }
17419     },
17420
17421     addClass : function(cls){
17422         if(this.elNode){
17423             Roo.fly(this.elNode).addClass(cls);
17424         }
17425     },
17426
17427     removeClass : function(cls){
17428         if(this.elNode){
17429             Roo.fly(this.elNode).removeClass(cls);
17430         }
17431     },
17432
17433     remove : function(){
17434         if(this.rendered){
17435             this.holder = document.createElement("div");
17436             this.holder.appendChild(this.wrap);
17437         }
17438     },
17439
17440     fireEvent : function(){
17441         return this.node.fireEvent.apply(this.node, arguments);
17442     },
17443
17444     initEvents : function(){
17445         this.node.on("move", this.onMove, this);
17446         var E = Roo.EventManager;
17447         var a = this.anchor;
17448
17449         var el = Roo.fly(a, '_treeui');
17450
17451         if(Roo.isOpera){ // opera render bug ignores the CSS
17452             el.setStyle("text-decoration", "none");
17453         }
17454
17455         el.on("click", this.onClick, this);
17456         el.on("dblclick", this.onDblClick, this);
17457
17458         if(this.checkbox){
17459             Roo.EventManager.on(this.checkbox,
17460                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17461         }
17462
17463         el.on("contextmenu", this.onContextMenu, this);
17464
17465         var icon = Roo.fly(this.iconNode);
17466         icon.on("click", this.onClick, this);
17467         icon.on("dblclick", this.onDblClick, this);
17468         icon.on("contextmenu", this.onContextMenu, this);
17469         E.on(this.ecNode, "click", this.ecClick, this, true);
17470
17471         if(this.node.disabled){
17472             this.addClass("x-tree-node-disabled");
17473         }
17474         if(this.node.hidden){
17475             this.addClass("x-tree-node-disabled");
17476         }
17477         var ot = this.node.getOwnerTree();
17478         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17479         if(dd && (!this.node.isRoot || ot.rootVisible)){
17480             Roo.dd.Registry.register(this.elNode, {
17481                 node: this.node,
17482                 handles: this.getDDHandles(),
17483                 isHandle: false
17484             });
17485         }
17486     },
17487
17488     getDDHandles : function(){
17489         return [this.iconNode, this.textNode];
17490     },
17491
17492     hide : function(){
17493         if(this.rendered){
17494             this.wrap.style.display = "none";
17495         }
17496     },
17497
17498     show : function(){
17499         if(this.rendered){
17500             this.wrap.style.display = "";
17501         }
17502     },
17503
17504     onContextMenu : function(e){
17505         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17506             e.preventDefault();
17507             this.focus();
17508             this.fireEvent("contextmenu", this.node, e);
17509         }
17510     },
17511
17512     onClick : function(e){
17513         if(this.dropping){
17514             e.stopEvent();
17515             return;
17516         }
17517         if(this.fireEvent("beforeclick", this.node, e) !== false){
17518             if(!this.disabled && this.node.attributes.href){
17519                 this.fireEvent("click", this.node, e);
17520                 return;
17521             }
17522             e.preventDefault();
17523             if(this.disabled){
17524                 return;
17525             }
17526
17527             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17528                 this.node.toggle();
17529             }
17530
17531             this.fireEvent("click", this.node, e);
17532         }else{
17533             e.stopEvent();
17534         }
17535     },
17536
17537     onDblClick : function(e){
17538         e.preventDefault();
17539         if(this.disabled){
17540             return;
17541         }
17542         if(this.checkbox){
17543             this.toggleCheck();
17544         }
17545         if(!this.animating && this.node.hasChildNodes()){
17546             this.node.toggle();
17547         }
17548         this.fireEvent("dblclick", this.node, e);
17549     },
17550
17551     onCheckChange : function(){
17552         var checked = this.checkbox.checked;
17553         this.node.attributes.checked = checked;
17554         this.fireEvent('checkchange', this.node, checked);
17555     },
17556
17557     ecClick : function(e){
17558         if(!this.animating && this.node.hasChildNodes()){
17559             this.node.toggle();
17560         }
17561     },
17562
17563     startDrop : function(){
17564         this.dropping = true;
17565     },
17566
17567     // delayed drop so the click event doesn't get fired on a drop
17568     endDrop : function(){
17569        setTimeout(function(){
17570            this.dropping = false;
17571        }.createDelegate(this), 50);
17572     },
17573
17574     expand : function(){
17575         this.updateExpandIcon();
17576         this.ctNode.style.display = "";
17577     },
17578
17579     focus : function(){
17580         if(!this.node.preventHScroll){
17581             try{this.anchor.focus();
17582             }catch(e){}
17583         }else if(!Roo.isIE){
17584             try{
17585                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17586                 var l = noscroll.scrollLeft;
17587                 this.anchor.focus();
17588                 noscroll.scrollLeft = l;
17589             }catch(e){}
17590         }
17591     },
17592
17593     toggleCheck : function(value){
17594         var cb = this.checkbox;
17595         if(cb){
17596             cb.checked = (value === undefined ? !cb.checked : value);
17597         }
17598     },
17599
17600     blur : function(){
17601         try{
17602             this.anchor.blur();
17603         }catch(e){}
17604     },
17605
17606     animExpand : function(callback){
17607         var ct = Roo.get(this.ctNode);
17608         ct.stopFx();
17609         if(!this.node.hasChildNodes()){
17610             this.updateExpandIcon();
17611             this.ctNode.style.display = "";
17612             Roo.callback(callback);
17613             return;
17614         }
17615         this.animating = true;
17616         this.updateExpandIcon();
17617
17618         ct.slideIn('t', {
17619            callback : function(){
17620                this.animating = false;
17621                Roo.callback(callback);
17622             },
17623             scope: this,
17624             duration: this.node.ownerTree.duration || .25
17625         });
17626     },
17627
17628     highlight : function(){
17629         var tree = this.node.getOwnerTree();
17630         Roo.fly(this.wrap).highlight(
17631             tree.hlColor || "C3DAF9",
17632             {endColor: tree.hlBaseColor}
17633         );
17634     },
17635
17636     collapse : function(){
17637         this.updateExpandIcon();
17638         this.ctNode.style.display = "none";
17639     },
17640
17641     animCollapse : function(callback){
17642         var ct = Roo.get(this.ctNode);
17643         ct.enableDisplayMode('block');
17644         ct.stopFx();
17645
17646         this.animating = true;
17647         this.updateExpandIcon();
17648
17649         ct.slideOut('t', {
17650             callback : function(){
17651                this.animating = false;
17652                Roo.callback(callback);
17653             },
17654             scope: this,
17655             duration: this.node.ownerTree.duration || .25
17656         });
17657     },
17658
17659     getContainer : function(){
17660         return this.ctNode;
17661     },
17662
17663     getEl : function(){
17664         return this.wrap;
17665     },
17666
17667     appendDDGhost : function(ghostNode){
17668         ghostNode.appendChild(this.elNode.cloneNode(true));
17669     },
17670
17671     getDDRepairXY : function(){
17672         return Roo.lib.Dom.getXY(this.iconNode);
17673     },
17674
17675     onRender : function(){
17676         this.render();
17677     },
17678
17679     render : function(bulkRender){
17680         var n = this.node, a = n.attributes;
17681         var targetNode = n.parentNode ?
17682               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17683
17684         if(!this.rendered){
17685             this.rendered = true;
17686
17687             this.renderElements(n, a, targetNode, bulkRender);
17688
17689             if(a.qtip){
17690                if(this.textNode.setAttributeNS){
17691                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17692                    if(a.qtipTitle){
17693                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17694                    }
17695                }else{
17696                    this.textNode.setAttribute("ext:qtip", a.qtip);
17697                    if(a.qtipTitle){
17698                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17699                    }
17700                }
17701             }else if(a.qtipCfg){
17702                 a.qtipCfg.target = Roo.id(this.textNode);
17703                 Roo.QuickTips.register(a.qtipCfg);
17704             }
17705             this.initEvents();
17706             if(!this.node.expanded){
17707                 this.updateExpandIcon();
17708             }
17709         }else{
17710             if(bulkRender === true) {
17711                 targetNode.appendChild(this.wrap);
17712             }
17713         }
17714     },
17715
17716     renderElements : function(n, a, targetNode, bulkRender)
17717     {
17718         // add some indent caching, this helps performance when rendering a large tree
17719         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17720         var t = n.getOwnerTree();
17721         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17722         if (typeof(n.attributes.html) != 'undefined') {
17723             txt = n.attributes.html;
17724         }
17725         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17726         var cb = typeof a.checked == 'boolean';
17727         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17728         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17729             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17730             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17731             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17732             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17733             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17734              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17735                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17736             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17737             "</li>"];
17738
17739         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17740             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17741                                 n.nextSibling.ui.getEl(), buf.join(""));
17742         }else{
17743             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17744         }
17745
17746         this.elNode = this.wrap.childNodes[0];
17747         this.ctNode = this.wrap.childNodes[1];
17748         var cs = this.elNode.childNodes;
17749         this.indentNode = cs[0];
17750         this.ecNode = cs[1];
17751         this.iconNode = cs[2];
17752         var index = 3;
17753         if(cb){
17754             this.checkbox = cs[3];
17755             index++;
17756         }
17757         this.anchor = cs[index];
17758         this.textNode = cs[index].firstChild;
17759     },
17760
17761     getAnchor : function(){
17762         return this.anchor;
17763     },
17764
17765     getTextEl : function(){
17766         return this.textNode;
17767     },
17768
17769     getIconEl : function(){
17770         return this.iconNode;
17771     },
17772
17773     isChecked : function(){
17774         return this.checkbox ? this.checkbox.checked : false;
17775     },
17776
17777     updateExpandIcon : function(){
17778         if(this.rendered){
17779             var n = this.node, c1, c2;
17780             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17781             var hasChild = n.hasChildNodes();
17782             if(hasChild){
17783                 if(n.expanded){
17784                     cls += "-minus";
17785                     c1 = "x-tree-node-collapsed";
17786                     c2 = "x-tree-node-expanded";
17787                 }else{
17788                     cls += "-plus";
17789                     c1 = "x-tree-node-expanded";
17790                     c2 = "x-tree-node-collapsed";
17791                 }
17792                 if(this.wasLeaf){
17793                     this.removeClass("x-tree-node-leaf");
17794                     this.wasLeaf = false;
17795                 }
17796                 if(this.c1 != c1 || this.c2 != c2){
17797                     Roo.fly(this.elNode).replaceClass(c1, c2);
17798                     this.c1 = c1; this.c2 = c2;
17799                 }
17800             }else{
17801                 // this changes non-leafs into leafs if they have no children.
17802                 // it's not very rational behaviour..
17803                 
17804                 if(!this.wasLeaf && this.node.leaf){
17805                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17806                     delete this.c1;
17807                     delete this.c2;
17808                     this.wasLeaf = true;
17809                 }
17810             }
17811             var ecc = "x-tree-ec-icon "+cls;
17812             if(this.ecc != ecc){
17813                 this.ecNode.className = ecc;
17814                 this.ecc = ecc;
17815             }
17816         }
17817     },
17818
17819     getChildIndent : function(){
17820         if(!this.childIndent){
17821             var buf = [];
17822             var p = this.node;
17823             while(p){
17824                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17825                     if(!p.isLast()) {
17826                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17827                     } else {
17828                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17829                     }
17830                 }
17831                 p = p.parentNode;
17832             }
17833             this.childIndent = buf.join("");
17834         }
17835         return this.childIndent;
17836     },
17837
17838     renderIndent : function(){
17839         if(this.rendered){
17840             var indent = "";
17841             var p = this.node.parentNode;
17842             if(p){
17843                 indent = p.ui.getChildIndent();
17844             }
17845             if(this.indentMarkup != indent){ // don't rerender if not required
17846                 this.indentNode.innerHTML = indent;
17847                 this.indentMarkup = indent;
17848             }
17849             this.updateExpandIcon();
17850         }
17851     }
17852 };
17853
17854 Roo.tree.RootTreeNodeUI = function(){
17855     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17856 };
17857 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17858     render : function(){
17859         if(!this.rendered){
17860             var targetNode = this.node.ownerTree.innerCt.dom;
17861             this.node.expanded = true;
17862             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17863             this.wrap = this.ctNode = targetNode.firstChild;
17864         }
17865     },
17866     collapse : function(){
17867     },
17868     expand : function(){
17869     }
17870 });/*
17871  * Based on:
17872  * Ext JS Library 1.1.1
17873  * Copyright(c) 2006-2007, Ext JS, LLC.
17874  *
17875  * Originally Released Under LGPL - original licence link has changed is not relivant.
17876  *
17877  * Fork - LGPL
17878  * <script type="text/javascript">
17879  */
17880 /**
17881  * @class Roo.tree.TreeLoader
17882  * @extends Roo.util.Observable
17883  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17884  * nodes from a specified URL. The response must be a javascript Array definition
17885  * who's elements are node definition objects. eg:
17886  * <pre><code>
17887 {  success : true,
17888    data :      [
17889    
17890     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17891     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17892     ]
17893 }
17894
17895
17896 </code></pre>
17897  * <br><br>
17898  * The old style respose with just an array is still supported, but not recommended.
17899  * <br><br>
17900  *
17901  * A server request is sent, and child nodes are loaded only when a node is expanded.
17902  * The loading node's id is passed to the server under the parameter name "node" to
17903  * enable the server to produce the correct child nodes.
17904  * <br><br>
17905  * To pass extra parameters, an event handler may be attached to the "beforeload"
17906  * event, and the parameters specified in the TreeLoader's baseParams property:
17907  * <pre><code>
17908     myTreeLoader.on("beforeload", function(treeLoader, node) {
17909         this.baseParams.category = node.attributes.category;
17910     }, this);
17911 </code></pre><
17912  * This would pass an HTTP parameter called "category" to the server containing
17913  * the value of the Node's "category" attribute.
17914  * @constructor
17915  * Creates a new Treeloader.
17916  * @param {Object} config A config object containing config properties.
17917  */
17918 Roo.tree.TreeLoader = function(config){
17919     this.baseParams = {};
17920     this.requestMethod = "POST";
17921     Roo.apply(this, config);
17922
17923     this.addEvents({
17924     
17925         /**
17926          * @event beforeload
17927          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17928          * @param {Object} This TreeLoader object.
17929          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17930          * @param {Object} callback The callback function specified in the {@link #load} call.
17931          */
17932         beforeload : true,
17933         /**
17934          * @event load
17935          * Fires when the node has been successfuly loaded.
17936          * @param {Object} This TreeLoader object.
17937          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17938          * @param {Object} response The response object containing the data from the server.
17939          */
17940         load : true,
17941         /**
17942          * @event loadexception
17943          * Fires if the network request failed.
17944          * @param {Object} This TreeLoader object.
17945          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17946          * @param {Object} response The response object containing the data from the server.
17947          */
17948         loadexception : true,
17949         /**
17950          * @event create
17951          * Fires before a node is created, enabling you to return custom Node types 
17952          * @param {Object} This TreeLoader object.
17953          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17954          */
17955         create : true
17956     });
17957
17958     Roo.tree.TreeLoader.superclass.constructor.call(this);
17959 };
17960
17961 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17962     /**
17963     * @cfg {String} dataUrl The URL from which to request a Json string which
17964     * specifies an array of node definition object representing the child nodes
17965     * to be loaded.
17966     */
17967     /**
17968     * @cfg {String} requestMethod either GET or POST
17969     * defaults to POST (due to BC)
17970     * to be loaded.
17971     */
17972     /**
17973     * @cfg {Object} baseParams (optional) An object containing properties which
17974     * specify HTTP parameters to be passed to each request for child nodes.
17975     */
17976     /**
17977     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17978     * created by this loader. If the attributes sent by the server have an attribute in this object,
17979     * they take priority.
17980     */
17981     /**
17982     * @cfg {Object} uiProviders (optional) An object containing properties which
17983     * 
17984     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17985     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17986     * <i>uiProvider</i> attribute of a returned child node is a string rather
17987     * than a reference to a TreeNodeUI implementation, this that string value
17988     * is used as a property name in the uiProviders object. You can define the provider named
17989     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17990     */
17991     uiProviders : {},
17992
17993     /**
17994     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17995     * child nodes before loading.
17996     */
17997     clearOnLoad : true,
17998
17999     /**
18000     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18001     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18002     * Grid query { data : [ .....] }
18003     */
18004     
18005     root : false,
18006      /**
18007     * @cfg {String} queryParam (optional) 
18008     * Name of the query as it will be passed on the querystring (defaults to 'node')
18009     * eg. the request will be ?node=[id]
18010     */
18011     
18012     
18013     queryParam: false,
18014     
18015     /**
18016      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18017      * This is called automatically when a node is expanded, but may be used to reload
18018      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18019      * @param {Roo.tree.TreeNode} node
18020      * @param {Function} callback
18021      */
18022     load : function(node, callback){
18023         if(this.clearOnLoad){
18024             while(node.firstChild){
18025                 node.removeChild(node.firstChild);
18026             }
18027         }
18028         if(node.attributes.children){ // preloaded json children
18029             var cs = node.attributes.children;
18030             for(var i = 0, len = cs.length; i < len; i++){
18031                 node.appendChild(this.createNode(cs[i]));
18032             }
18033             if(typeof callback == "function"){
18034                 callback();
18035             }
18036         }else if(this.dataUrl){
18037             this.requestData(node, callback);
18038         }
18039     },
18040
18041     getParams: function(node){
18042         var buf = [], bp = this.baseParams;
18043         for(var key in bp){
18044             if(typeof bp[key] != "function"){
18045                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18046             }
18047         }
18048         var n = this.queryParam === false ? 'node' : this.queryParam;
18049         buf.push(n + "=", encodeURIComponent(node.id));
18050         return buf.join("");
18051     },
18052
18053     requestData : function(node, callback){
18054         if(this.fireEvent("beforeload", this, node, callback) !== false){
18055             this.transId = Roo.Ajax.request({
18056                 method:this.requestMethod,
18057                 url: this.dataUrl||this.url,
18058                 success: this.handleResponse,
18059                 failure: this.handleFailure,
18060                 scope: this,
18061                 argument: {callback: callback, node: node},
18062                 params: this.getParams(node)
18063             });
18064         }else{
18065             // if the load is cancelled, make sure we notify
18066             // the node that we are done
18067             if(typeof callback == "function"){
18068                 callback();
18069             }
18070         }
18071     },
18072
18073     isLoading : function(){
18074         return this.transId ? true : false;
18075     },
18076
18077     abort : function(){
18078         if(this.isLoading()){
18079             Roo.Ajax.abort(this.transId);
18080         }
18081     },
18082
18083     // private
18084     createNode : function(attr)
18085     {
18086         // apply baseAttrs, nice idea Corey!
18087         if(this.baseAttrs){
18088             Roo.applyIf(attr, this.baseAttrs);
18089         }
18090         if(this.applyLoader !== false){
18091             attr.loader = this;
18092         }
18093         // uiProvider = depreciated..
18094         
18095         if(typeof(attr.uiProvider) == 'string'){
18096            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18097                 /**  eval:var:attr */ eval(attr.uiProvider);
18098         }
18099         if(typeof(this.uiProviders['default']) != 'undefined') {
18100             attr.uiProvider = this.uiProviders['default'];
18101         }
18102         
18103         this.fireEvent('create', this, attr);
18104         
18105         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18106         return(attr.leaf ?
18107                         new Roo.tree.TreeNode(attr) :
18108                         new Roo.tree.AsyncTreeNode(attr));
18109     },
18110
18111     processResponse : function(response, node, callback)
18112     {
18113         var json = response.responseText;
18114         try {
18115             
18116             var o = Roo.decode(json);
18117             
18118             if (this.root === false && typeof(o.success) != undefined) {
18119                 this.root = 'data'; // the default behaviour for list like data..
18120                 }
18121                 
18122             if (this.root !== false &&  !o.success) {
18123                 // it's a failure condition.
18124                 var a = response.argument;
18125                 this.fireEvent("loadexception", this, a.node, response);
18126                 Roo.log("Load failed - should have a handler really");
18127                 return;
18128             }
18129             
18130             
18131             
18132             if (this.root !== false) {
18133                  o = o[this.root];
18134             }
18135             
18136             for(var i = 0, len = o.length; i < len; i++){
18137                 var n = this.createNode(o[i]);
18138                 if(n){
18139                     node.appendChild(n);
18140                 }
18141             }
18142             if(typeof callback == "function"){
18143                 callback(this, node);
18144             }
18145         }catch(e){
18146             this.handleFailure(response);
18147         }
18148     },
18149
18150     handleResponse : function(response){
18151         this.transId = false;
18152         var a = response.argument;
18153         this.processResponse(response, a.node, a.callback);
18154         this.fireEvent("load", this, a.node, response);
18155     },
18156
18157     handleFailure : function(response)
18158     {
18159         // should handle failure better..
18160         this.transId = false;
18161         var a = response.argument;
18162         this.fireEvent("loadexception", this, a.node, response);
18163         if(typeof a.callback == "function"){
18164             a.callback(this, a.node);
18165         }
18166     }
18167 });/*
18168  * Based on:
18169  * Ext JS Library 1.1.1
18170  * Copyright(c) 2006-2007, Ext JS, LLC.
18171  *
18172  * Originally Released Under LGPL - original licence link has changed is not relivant.
18173  *
18174  * Fork - LGPL
18175  * <script type="text/javascript">
18176  */
18177
18178 /**
18179 * @class Roo.tree.TreeFilter
18180 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18181 * @param {TreePanel} tree
18182 * @param {Object} config (optional)
18183  */
18184 Roo.tree.TreeFilter = function(tree, config){
18185     this.tree = tree;
18186     this.filtered = {};
18187     Roo.apply(this, config);
18188 };
18189
18190 Roo.tree.TreeFilter.prototype = {
18191     clearBlank:false,
18192     reverse:false,
18193     autoClear:false,
18194     remove:false,
18195
18196      /**
18197      * Filter the data by a specific attribute.
18198      * @param {String/RegExp} value Either string that the attribute value
18199      * should start with or a RegExp to test against the attribute
18200      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18201      * @param {TreeNode} startNode (optional) The node to start the filter at.
18202      */
18203     filter : function(value, attr, startNode){
18204         attr = attr || "text";
18205         var f;
18206         if(typeof value == "string"){
18207             var vlen = value.length;
18208             // auto clear empty filter
18209             if(vlen == 0 && this.clearBlank){
18210                 this.clear();
18211                 return;
18212             }
18213             value = value.toLowerCase();
18214             f = function(n){
18215                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18216             };
18217         }else if(value.exec){ // regex?
18218             f = function(n){
18219                 return value.test(n.attributes[attr]);
18220             };
18221         }else{
18222             throw 'Illegal filter type, must be string or regex';
18223         }
18224         this.filterBy(f, null, startNode);
18225         },
18226
18227     /**
18228      * Filter by a function. The passed function will be called with each
18229      * node in the tree (or from the startNode). If the function returns true, the node is kept
18230      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18231      * @param {Function} fn The filter function
18232      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18233      */
18234     filterBy : function(fn, scope, startNode){
18235         startNode = startNode || this.tree.root;
18236         if(this.autoClear){
18237             this.clear();
18238         }
18239         var af = this.filtered, rv = this.reverse;
18240         var f = function(n){
18241             if(n == startNode){
18242                 return true;
18243             }
18244             if(af[n.id]){
18245                 return false;
18246             }
18247             var m = fn.call(scope || n, n);
18248             if(!m || rv){
18249                 af[n.id] = n;
18250                 n.ui.hide();
18251                 return false;
18252             }
18253             return true;
18254         };
18255         startNode.cascade(f);
18256         if(this.remove){
18257            for(var id in af){
18258                if(typeof id != "function"){
18259                    var n = af[id];
18260                    if(n && n.parentNode){
18261                        n.parentNode.removeChild(n);
18262                    }
18263                }
18264            }
18265         }
18266     },
18267
18268     /**
18269      * Clears the current filter. Note: with the "remove" option
18270      * set a filter cannot be cleared.
18271      */
18272     clear : function(){
18273         var t = this.tree;
18274         var af = this.filtered;
18275         for(var id in af){
18276             if(typeof id != "function"){
18277                 var n = af[id];
18278                 if(n){
18279                     n.ui.show();
18280                 }
18281             }
18282         }
18283         this.filtered = {};
18284     }
18285 };
18286 /*
18287  * Based on:
18288  * Ext JS Library 1.1.1
18289  * Copyright(c) 2006-2007, Ext JS, LLC.
18290  *
18291  * Originally Released Under LGPL - original licence link has changed is not relivant.
18292  *
18293  * Fork - LGPL
18294  * <script type="text/javascript">
18295  */
18296  
18297
18298 /**
18299  * @class Roo.tree.TreeSorter
18300  * Provides sorting of nodes in a TreePanel
18301  * 
18302  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18303  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18304  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18305  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18306  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18307  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18308  * @constructor
18309  * @param {TreePanel} tree
18310  * @param {Object} config
18311  */
18312 Roo.tree.TreeSorter = function(tree, config){
18313     Roo.apply(this, config);
18314     tree.on("beforechildrenrendered", this.doSort, this);
18315     tree.on("append", this.updateSort, this);
18316     tree.on("insert", this.updateSort, this);
18317     
18318     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18319     var p = this.property || "text";
18320     var sortType = this.sortType;
18321     var fs = this.folderSort;
18322     var cs = this.caseSensitive === true;
18323     var leafAttr = this.leafAttr || 'leaf';
18324
18325     this.sortFn = function(n1, n2){
18326         if(fs){
18327             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18328                 return 1;
18329             }
18330             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18331                 return -1;
18332             }
18333         }
18334         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18335         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18336         if(v1 < v2){
18337                         return dsc ? +1 : -1;
18338                 }else if(v1 > v2){
18339                         return dsc ? -1 : +1;
18340         }else{
18341                 return 0;
18342         }
18343     };
18344 };
18345
18346 Roo.tree.TreeSorter.prototype = {
18347     doSort : function(node){
18348         node.sort(this.sortFn);
18349     },
18350     
18351     compareNodes : function(n1, n2){
18352         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18353     },
18354     
18355     updateSort : function(tree, node){
18356         if(node.childrenRendered){
18357             this.doSort.defer(1, this, [node]);
18358         }
18359     }
18360 };/*
18361  * Based on:
18362  * Ext JS Library 1.1.1
18363  * Copyright(c) 2006-2007, Ext JS, LLC.
18364  *
18365  * Originally Released Under LGPL - original licence link has changed is not relivant.
18366  *
18367  * Fork - LGPL
18368  * <script type="text/javascript">
18369  */
18370
18371 if(Roo.dd.DropZone){
18372     
18373 Roo.tree.TreeDropZone = function(tree, config){
18374     this.allowParentInsert = false;
18375     this.allowContainerDrop = false;
18376     this.appendOnly = false;
18377     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18378     this.tree = tree;
18379     this.lastInsertClass = "x-tree-no-status";
18380     this.dragOverData = {};
18381 };
18382
18383 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18384     ddGroup : "TreeDD",
18385     scroll:  true,
18386     
18387     expandDelay : 1000,
18388     
18389     expandNode : function(node){
18390         if(node.hasChildNodes() && !node.isExpanded()){
18391             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18392         }
18393     },
18394     
18395     queueExpand : function(node){
18396         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18397     },
18398     
18399     cancelExpand : function(){
18400         if(this.expandProcId){
18401             clearTimeout(this.expandProcId);
18402             this.expandProcId = false;
18403         }
18404     },
18405     
18406     isValidDropPoint : function(n, pt, dd, e, data){
18407         if(!n || !data){ return false; }
18408         var targetNode = n.node;
18409         var dropNode = data.node;
18410         // default drop rules
18411         if(!(targetNode && targetNode.isTarget && pt)){
18412             return false;
18413         }
18414         if(pt == "append" && targetNode.allowChildren === false){
18415             return false;
18416         }
18417         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18418             return false;
18419         }
18420         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18421             return false;
18422         }
18423         // reuse the object
18424         var overEvent = this.dragOverData;
18425         overEvent.tree = this.tree;
18426         overEvent.target = targetNode;
18427         overEvent.data = data;
18428         overEvent.point = pt;
18429         overEvent.source = dd;
18430         overEvent.rawEvent = e;
18431         overEvent.dropNode = dropNode;
18432         overEvent.cancel = false;  
18433         var result = this.tree.fireEvent("nodedragover", overEvent);
18434         return overEvent.cancel === false && result !== false;
18435     },
18436     
18437     getDropPoint : function(e, n, dd)
18438     {
18439         var tn = n.node;
18440         if(tn.isRoot){
18441             return tn.allowChildren !== false ? "append" : false; // always append for root
18442         }
18443         var dragEl = n.ddel;
18444         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18445         var y = Roo.lib.Event.getPageY(e);
18446         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18447         
18448         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18449         var noAppend = tn.allowChildren === false;
18450         if(this.appendOnly || tn.parentNode.allowChildren === false){
18451             return noAppend ? false : "append";
18452         }
18453         var noBelow = false;
18454         if(!this.allowParentInsert){
18455             noBelow = tn.hasChildNodes() && tn.isExpanded();
18456         }
18457         var q = (b - t) / (noAppend ? 2 : 3);
18458         if(y >= t && y < (t + q)){
18459             return "above";
18460         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18461             return "below";
18462         }else{
18463             return "append";
18464         }
18465     },
18466     
18467     onNodeEnter : function(n, dd, e, data)
18468     {
18469         this.cancelExpand();
18470     },
18471     
18472     onNodeOver : function(n, dd, e, data)
18473     {
18474        
18475         var pt = this.getDropPoint(e, n, dd);
18476         var node = n.node;
18477         
18478         // auto node expand check
18479         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18480             this.queueExpand(node);
18481         }else if(pt != "append"){
18482             this.cancelExpand();
18483         }
18484         
18485         // set the insert point style on the target node
18486         var returnCls = this.dropNotAllowed;
18487         if(this.isValidDropPoint(n, pt, dd, e, data)){
18488            if(pt){
18489                var el = n.ddel;
18490                var cls;
18491                if(pt == "above"){
18492                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18493                    cls = "x-tree-drag-insert-above";
18494                }else if(pt == "below"){
18495                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18496                    cls = "x-tree-drag-insert-below";
18497                }else{
18498                    returnCls = "x-tree-drop-ok-append";
18499                    cls = "x-tree-drag-append";
18500                }
18501                if(this.lastInsertClass != cls){
18502                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18503                    this.lastInsertClass = cls;
18504                }
18505            }
18506        }
18507        return returnCls;
18508     },
18509     
18510     onNodeOut : function(n, dd, e, data){
18511         
18512         this.cancelExpand();
18513         this.removeDropIndicators(n);
18514     },
18515     
18516     onNodeDrop : function(n, dd, e, data){
18517         var point = this.getDropPoint(e, n, dd);
18518         var targetNode = n.node;
18519         targetNode.ui.startDrop();
18520         if(!this.isValidDropPoint(n, point, dd, e, data)){
18521             targetNode.ui.endDrop();
18522             return false;
18523         }
18524         // first try to find the drop node
18525         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18526         var dropEvent = {
18527             tree : this.tree,
18528             target: targetNode,
18529             data: data,
18530             point: point,
18531             source: dd,
18532             rawEvent: e,
18533             dropNode: dropNode,
18534             cancel: !dropNode   
18535         };
18536         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18537         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18538             targetNode.ui.endDrop();
18539             return false;
18540         }
18541         // allow target changing
18542         targetNode = dropEvent.target;
18543         if(point == "append" && !targetNode.isExpanded()){
18544             targetNode.expand(false, null, function(){
18545                 this.completeDrop(dropEvent);
18546             }.createDelegate(this));
18547         }else{
18548             this.completeDrop(dropEvent);
18549         }
18550         return true;
18551     },
18552     
18553     completeDrop : function(de){
18554         var ns = de.dropNode, p = de.point, t = de.target;
18555         if(!(ns instanceof Array)){
18556             ns = [ns];
18557         }
18558         var n;
18559         for(var i = 0, len = ns.length; i < len; i++){
18560             n = ns[i];
18561             if(p == "above"){
18562                 t.parentNode.insertBefore(n, t);
18563             }else if(p == "below"){
18564                 t.parentNode.insertBefore(n, t.nextSibling);
18565             }else{
18566                 t.appendChild(n);
18567             }
18568         }
18569         n.ui.focus();
18570         if(this.tree.hlDrop){
18571             n.ui.highlight();
18572         }
18573         t.ui.endDrop();
18574         this.tree.fireEvent("nodedrop", de);
18575     },
18576     
18577     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18578         if(this.tree.hlDrop){
18579             dropNode.ui.focus();
18580             dropNode.ui.highlight();
18581         }
18582         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18583     },
18584     
18585     getTree : function(){
18586         return this.tree;
18587     },
18588     
18589     removeDropIndicators : function(n){
18590         if(n && n.ddel){
18591             var el = n.ddel;
18592             Roo.fly(el).removeClass([
18593                     "x-tree-drag-insert-above",
18594                     "x-tree-drag-insert-below",
18595                     "x-tree-drag-append"]);
18596             this.lastInsertClass = "_noclass";
18597         }
18598     },
18599     
18600     beforeDragDrop : function(target, e, id){
18601         this.cancelExpand();
18602         return true;
18603     },
18604     
18605     afterRepair : function(data){
18606         if(data && Roo.enableFx){
18607             data.node.ui.highlight();
18608         }
18609         this.hideProxy();
18610     } 
18611     
18612 });
18613
18614 }
18615 /*
18616  * Based on:
18617  * Ext JS Library 1.1.1
18618  * Copyright(c) 2006-2007, Ext JS, LLC.
18619  *
18620  * Originally Released Under LGPL - original licence link has changed is not relivant.
18621  *
18622  * Fork - LGPL
18623  * <script type="text/javascript">
18624  */
18625  
18626
18627 if(Roo.dd.DragZone){
18628 Roo.tree.TreeDragZone = function(tree, config){
18629     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18630     this.tree = tree;
18631 };
18632
18633 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18634     ddGroup : "TreeDD",
18635    
18636     onBeforeDrag : function(data, e){
18637         var n = data.node;
18638         return n && n.draggable && !n.disabled;
18639     },
18640      
18641     
18642     onInitDrag : function(e){
18643         var data = this.dragData;
18644         this.tree.getSelectionModel().select(data.node);
18645         this.proxy.update("");
18646         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18647         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18648     },
18649     
18650     getRepairXY : function(e, data){
18651         return data.node.ui.getDDRepairXY();
18652     },
18653     
18654     onEndDrag : function(data, e){
18655         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18656         
18657         
18658     },
18659     
18660     onValidDrop : function(dd, e, id){
18661         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18662         this.hideProxy();
18663     },
18664     
18665     beforeInvalidDrop : function(e, id){
18666         // this scrolls the original position back into view
18667         var sm = this.tree.getSelectionModel();
18668         sm.clearSelections();
18669         sm.select(this.dragData.node);
18670     }
18671 });
18672 }/*
18673  * Based on:
18674  * Ext JS Library 1.1.1
18675  * Copyright(c) 2006-2007, Ext JS, LLC.
18676  *
18677  * Originally Released Under LGPL - original licence link has changed is not relivant.
18678  *
18679  * Fork - LGPL
18680  * <script type="text/javascript">
18681  */
18682 /**
18683  * @class Roo.tree.TreeEditor
18684  * @extends Roo.Editor
18685  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18686  * as the editor field.
18687  * @constructor
18688  * @param {Object} config (used to be the tree panel.)
18689  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18690  * 
18691  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18692  * @cfg {Roo.form.TextField|Object} field The field configuration
18693  *
18694  * 
18695  */
18696 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18697     var tree = config;
18698     var field;
18699     if (oldconfig) { // old style..
18700         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18701     } else {
18702         // new style..
18703         tree = config.tree;
18704         config.field = config.field  || {};
18705         config.field.xtype = 'TextField';
18706         field = Roo.factory(config.field, Roo.form);
18707     }
18708     config = config || {};
18709     
18710     
18711     this.addEvents({
18712         /**
18713          * @event beforenodeedit
18714          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18715          * false from the handler of this event.
18716          * @param {Editor} this
18717          * @param {Roo.tree.Node} node 
18718          */
18719         "beforenodeedit" : true
18720     });
18721     
18722     //Roo.log(config);
18723     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18724
18725     this.tree = tree;
18726
18727     tree.on('beforeclick', this.beforeNodeClick, this);
18728     tree.getTreeEl().on('mousedown', this.hide, this);
18729     this.on('complete', this.updateNode, this);
18730     this.on('beforestartedit', this.fitToTree, this);
18731     this.on('startedit', this.bindScroll, this, {delay:10});
18732     this.on('specialkey', this.onSpecialKey, this);
18733 };
18734
18735 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18736     /**
18737      * @cfg {String} alignment
18738      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18739      */
18740     alignment: "l-l",
18741     // inherit
18742     autoSize: false,
18743     /**
18744      * @cfg {Boolean} hideEl
18745      * True to hide the bound element while the editor is displayed (defaults to false)
18746      */
18747     hideEl : false,
18748     /**
18749      * @cfg {String} cls
18750      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18751      */
18752     cls: "x-small-editor x-tree-editor",
18753     /**
18754      * @cfg {Boolean} shim
18755      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18756      */
18757     shim:false,
18758     // inherit
18759     shadow:"frame",
18760     /**
18761      * @cfg {Number} maxWidth
18762      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18763      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18764      * scroll and client offsets into account prior to each edit.
18765      */
18766     maxWidth: 250,
18767
18768     editDelay : 350,
18769
18770     // private
18771     fitToTree : function(ed, el){
18772         var td = this.tree.getTreeEl().dom, nd = el.dom;
18773         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18774             td.scrollLeft = nd.offsetLeft;
18775         }
18776         var w = Math.min(
18777                 this.maxWidth,
18778                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18779         this.setSize(w, '');
18780         
18781         return this.fireEvent('beforenodeedit', this, this.editNode);
18782         
18783     },
18784
18785     // private
18786     triggerEdit : function(node){
18787         this.completeEdit();
18788         this.editNode = node;
18789         this.startEdit(node.ui.textNode, node.text);
18790     },
18791
18792     // private
18793     bindScroll : function(){
18794         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18795     },
18796
18797     // private
18798     beforeNodeClick : function(node, e){
18799         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18800         this.lastClick = new Date();
18801         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18802             e.stopEvent();
18803             this.triggerEdit(node);
18804             return false;
18805         }
18806         return true;
18807     },
18808
18809     // private
18810     updateNode : function(ed, value){
18811         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18812         this.editNode.setText(value);
18813     },
18814
18815     // private
18816     onHide : function(){
18817         Roo.tree.TreeEditor.superclass.onHide.call(this);
18818         if(this.editNode){
18819             this.editNode.ui.focus();
18820         }
18821     },
18822
18823     // private
18824     onSpecialKey : function(field, e){
18825         var k = e.getKey();
18826         if(k == e.ESC){
18827             e.stopEvent();
18828             this.cancelEdit();
18829         }else if(k == e.ENTER && !e.hasModifier()){
18830             e.stopEvent();
18831             this.completeEdit();
18832         }
18833     }
18834 });//<Script type="text/javascript">
18835 /*
18836  * Based on:
18837  * Ext JS Library 1.1.1
18838  * Copyright(c) 2006-2007, Ext JS, LLC.
18839  *
18840  * Originally Released Under LGPL - original licence link has changed is not relivant.
18841  *
18842  * Fork - LGPL
18843  * <script type="text/javascript">
18844  */
18845  
18846 /**
18847  * Not documented??? - probably should be...
18848  */
18849
18850 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18851     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18852     
18853     renderElements : function(n, a, targetNode, bulkRender){
18854         //consel.log("renderElements?");
18855         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18856
18857         var t = n.getOwnerTree();
18858         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18859         
18860         var cols = t.columns;
18861         var bw = t.borderWidth;
18862         var c = cols[0];
18863         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18864          var cb = typeof a.checked == "boolean";
18865         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18866         var colcls = 'x-t-' + tid + '-c0';
18867         var buf = [
18868             '<li class="x-tree-node">',
18869             
18870                 
18871                 '<div class="x-tree-node-el ', a.cls,'">',
18872                     // extran...
18873                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18874                 
18875                 
18876                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18877                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18878                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18879                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18880                            (a.iconCls ? ' '+a.iconCls : ''),
18881                            '" unselectable="on" />',
18882                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18883                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18884                              
18885                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18886                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18887                             '<span unselectable="on" qtip="' + tx + '">',
18888                              tx,
18889                              '</span></a>' ,
18890                     '</div>',
18891                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18892                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18893                  ];
18894         for(var i = 1, len = cols.length; i < len; i++){
18895             c = cols[i];
18896             colcls = 'x-t-' + tid + '-c' +i;
18897             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18898             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18899                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18900                       "</div>");
18901          }
18902          
18903          buf.push(
18904             '</a>',
18905             '<div class="x-clear"></div></div>',
18906             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18907             "</li>");
18908         
18909         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18910             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18911                                 n.nextSibling.ui.getEl(), buf.join(""));
18912         }else{
18913             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18914         }
18915         var el = this.wrap.firstChild;
18916         this.elRow = el;
18917         this.elNode = el.firstChild;
18918         this.ranchor = el.childNodes[1];
18919         this.ctNode = this.wrap.childNodes[1];
18920         var cs = el.firstChild.childNodes;
18921         this.indentNode = cs[0];
18922         this.ecNode = cs[1];
18923         this.iconNode = cs[2];
18924         var index = 3;
18925         if(cb){
18926             this.checkbox = cs[3];
18927             index++;
18928         }
18929         this.anchor = cs[index];
18930         
18931         this.textNode = cs[index].firstChild;
18932         
18933         //el.on("click", this.onClick, this);
18934         //el.on("dblclick", this.onDblClick, this);
18935         
18936         
18937        // console.log(this);
18938     },
18939     initEvents : function(){
18940         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18941         
18942             
18943         var a = this.ranchor;
18944
18945         var el = Roo.get(a);
18946
18947         if(Roo.isOpera){ // opera render bug ignores the CSS
18948             el.setStyle("text-decoration", "none");
18949         }
18950
18951         el.on("click", this.onClick, this);
18952         el.on("dblclick", this.onDblClick, this);
18953         el.on("contextmenu", this.onContextMenu, this);
18954         
18955     },
18956     
18957     /*onSelectedChange : function(state){
18958         if(state){
18959             this.focus();
18960             this.addClass("x-tree-selected");
18961         }else{
18962             //this.blur();
18963             this.removeClass("x-tree-selected");
18964         }
18965     },*/
18966     addClass : function(cls){
18967         if(this.elRow){
18968             Roo.fly(this.elRow).addClass(cls);
18969         }
18970         
18971     },
18972     
18973     
18974     removeClass : function(cls){
18975         if(this.elRow){
18976             Roo.fly(this.elRow).removeClass(cls);
18977         }
18978     }
18979
18980     
18981     
18982 });//<Script type="text/javascript">
18983
18984 /*
18985  * Based on:
18986  * Ext JS Library 1.1.1
18987  * Copyright(c) 2006-2007, Ext JS, LLC.
18988  *
18989  * Originally Released Under LGPL - original licence link has changed is not relivant.
18990  *
18991  * Fork - LGPL
18992  * <script type="text/javascript">
18993  */
18994  
18995
18996 /**
18997  * @class Roo.tree.ColumnTree
18998  * @extends Roo.data.TreePanel
18999  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19000  * @cfg {int} borderWidth  compined right/left border allowance
19001  * @constructor
19002  * @param {String/HTMLElement/Element} el The container element
19003  * @param {Object} config
19004  */
19005 Roo.tree.ColumnTree =  function(el, config)
19006 {
19007    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19008    this.addEvents({
19009         /**
19010         * @event resize
19011         * Fire this event on a container when it resizes
19012         * @param {int} w Width
19013         * @param {int} h Height
19014         */
19015        "resize" : true
19016     });
19017     this.on('resize', this.onResize, this);
19018 };
19019
19020 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19021     //lines:false,
19022     
19023     
19024     borderWidth: Roo.isBorderBox ? 0 : 2, 
19025     headEls : false,
19026     
19027     render : function(){
19028         // add the header.....
19029        
19030         Roo.tree.ColumnTree.superclass.render.apply(this);
19031         
19032         this.el.addClass('x-column-tree');
19033         
19034         this.headers = this.el.createChild(
19035             {cls:'x-tree-headers'},this.innerCt.dom);
19036    
19037         var cols = this.columns, c;
19038         var totalWidth = 0;
19039         this.headEls = [];
19040         var  len = cols.length;
19041         for(var i = 0; i < len; i++){
19042              c = cols[i];
19043              totalWidth += c.width;
19044             this.headEls.push(this.headers.createChild({
19045                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19046                  cn: {
19047                      cls:'x-tree-hd-text',
19048                      html: c.header
19049                  },
19050                  style:'width:'+(c.width-this.borderWidth)+'px;'
19051              }));
19052         }
19053         this.headers.createChild({cls:'x-clear'});
19054         // prevent floats from wrapping when clipped
19055         this.headers.setWidth(totalWidth);
19056         //this.innerCt.setWidth(totalWidth);
19057         this.innerCt.setStyle({ overflow: 'auto' });
19058         this.onResize(this.width, this.height);
19059              
19060         
19061     },
19062     onResize : function(w,h)
19063     {
19064         this.height = h;
19065         this.width = w;
19066         // resize cols..
19067         this.innerCt.setWidth(this.width);
19068         this.innerCt.setHeight(this.height-20);
19069         
19070         // headers...
19071         var cols = this.columns, c;
19072         var totalWidth = 0;
19073         var expEl = false;
19074         var len = cols.length;
19075         for(var i = 0; i < len; i++){
19076             c = cols[i];
19077             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19078                 // it's the expander..
19079                 expEl  = this.headEls[i];
19080                 continue;
19081             }
19082             totalWidth += c.width;
19083             
19084         }
19085         if (expEl) {
19086             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19087         }
19088         this.headers.setWidth(w-20);
19089
19090         
19091         
19092         
19093     }
19094 });
19095 /*
19096  * Based on:
19097  * Ext JS Library 1.1.1
19098  * Copyright(c) 2006-2007, Ext JS, LLC.
19099  *
19100  * Originally Released Under LGPL - original licence link has changed is not relivant.
19101  *
19102  * Fork - LGPL
19103  * <script type="text/javascript">
19104  */
19105  
19106 /**
19107  * @class Roo.menu.Menu
19108  * @extends Roo.util.Observable
19109  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19110  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19111  * @constructor
19112  * Creates a new Menu
19113  * @param {Object} config Configuration options
19114  */
19115 Roo.menu.Menu = function(config){
19116     Roo.apply(this, config);
19117     this.id = this.id || Roo.id();
19118     this.addEvents({
19119         /**
19120          * @event beforeshow
19121          * Fires before this menu is displayed
19122          * @param {Roo.menu.Menu} this
19123          */
19124         beforeshow : true,
19125         /**
19126          * @event beforehide
19127          * Fires before this menu is hidden
19128          * @param {Roo.menu.Menu} this
19129          */
19130         beforehide : true,
19131         /**
19132          * @event show
19133          * Fires after this menu is displayed
19134          * @param {Roo.menu.Menu} this
19135          */
19136         show : true,
19137         /**
19138          * @event hide
19139          * Fires after this menu is hidden
19140          * @param {Roo.menu.Menu} this
19141          */
19142         hide : true,
19143         /**
19144          * @event click
19145          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19146          * @param {Roo.menu.Menu} this
19147          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19148          * @param {Roo.EventObject} e
19149          */
19150         click : true,
19151         /**
19152          * @event mouseover
19153          * Fires when the mouse is hovering over this menu
19154          * @param {Roo.menu.Menu} this
19155          * @param {Roo.EventObject} e
19156          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19157          */
19158         mouseover : true,
19159         /**
19160          * @event mouseout
19161          * Fires when the mouse exits this menu
19162          * @param {Roo.menu.Menu} this
19163          * @param {Roo.EventObject} e
19164          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19165          */
19166         mouseout : true,
19167         /**
19168          * @event itemclick
19169          * Fires when a menu item contained in this menu is clicked
19170          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19171          * @param {Roo.EventObject} e
19172          */
19173         itemclick: true
19174     });
19175     if (this.registerMenu) {
19176         Roo.menu.MenuMgr.register(this);
19177     }
19178     
19179     var mis = this.items;
19180     this.items = new Roo.util.MixedCollection();
19181     if(mis){
19182         this.add.apply(this, mis);
19183     }
19184 };
19185
19186 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19187     /**
19188      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19189      */
19190     minWidth : 120,
19191     /**
19192      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19193      * for bottom-right shadow (defaults to "sides")
19194      */
19195     shadow : "sides",
19196     /**
19197      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19198      * this menu (defaults to "tl-tr?")
19199      */
19200     subMenuAlign : "tl-tr?",
19201     /**
19202      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19203      * relative to its element of origin (defaults to "tl-bl?")
19204      */
19205     defaultAlign : "tl-bl?",
19206     /**
19207      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19208      */
19209     allowOtherMenus : false,
19210     /**
19211      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19212      */
19213     registerMenu : true,
19214
19215     hidden:true,
19216
19217     // private
19218     render : function(){
19219         if(this.el){
19220             return;
19221         }
19222         var el = this.el = new Roo.Layer({
19223             cls: "x-menu",
19224             shadow:this.shadow,
19225             constrain: false,
19226             parentEl: this.parentEl || document.body,
19227             zindex:15000
19228         });
19229
19230         this.keyNav = new Roo.menu.MenuNav(this);
19231
19232         if(this.plain){
19233             el.addClass("x-menu-plain");
19234         }
19235         if(this.cls){
19236             el.addClass(this.cls);
19237         }
19238         // generic focus element
19239         this.focusEl = el.createChild({
19240             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19241         });
19242         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19243         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19244         
19245         ul.on("mouseover", this.onMouseOver, this);
19246         ul.on("mouseout", this.onMouseOut, this);
19247         this.items.each(function(item){
19248             if (item.hidden) {
19249                 return;
19250             }
19251             
19252             var li = document.createElement("li");
19253             li.className = "x-menu-list-item";
19254             ul.dom.appendChild(li);
19255             item.render(li, this);
19256         }, this);
19257         this.ul = ul;
19258         this.autoWidth();
19259     },
19260
19261     // private
19262     autoWidth : function(){
19263         var el = this.el, ul = this.ul;
19264         if(!el){
19265             return;
19266         }
19267         var w = this.width;
19268         if(w){
19269             el.setWidth(w);
19270         }else if(Roo.isIE){
19271             el.setWidth(this.minWidth);
19272             var t = el.dom.offsetWidth; // force recalc
19273             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19274         }
19275     },
19276
19277     // private
19278     delayAutoWidth : function(){
19279         if(this.rendered){
19280             if(!this.awTask){
19281                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19282             }
19283             this.awTask.delay(20);
19284         }
19285     },
19286
19287     // private
19288     findTargetItem : function(e){
19289         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19290         if(t && t.menuItemId){
19291             return this.items.get(t.menuItemId);
19292         }
19293     },
19294
19295     // private
19296     onClick : function(e){
19297         Roo.log("menu.onClick");
19298         var t = this.findTargetItem(e);
19299         if(!t){
19300             return;
19301         }
19302         Roo.log(e);
19303         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19304             if(t == this.activeItem && t.shouldDeactivate(e)){
19305                 this.activeItem.deactivate();
19306                 delete this.activeItem;
19307                 return;
19308             }
19309             if(t.canActivate){
19310                 this.setActiveItem(t, true);
19311             }
19312             return;
19313             
19314             
19315         }
19316         
19317         t.onClick(e);
19318         this.fireEvent("click", this, t, e);
19319     },
19320
19321     // private
19322     setActiveItem : function(item, autoExpand){
19323         if(item != this.activeItem){
19324             if(this.activeItem){
19325                 this.activeItem.deactivate();
19326             }
19327             this.activeItem = item;
19328             item.activate(autoExpand);
19329         }else if(autoExpand){
19330             item.expandMenu();
19331         }
19332     },
19333
19334     // private
19335     tryActivate : function(start, step){
19336         var items = this.items;
19337         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19338             var item = items.get(i);
19339             if(!item.disabled && item.canActivate){
19340                 this.setActiveItem(item, false);
19341                 return item;
19342             }
19343         }
19344         return false;
19345     },
19346
19347     // private
19348     onMouseOver : function(e){
19349         var t;
19350         if(t = this.findTargetItem(e)){
19351             if(t.canActivate && !t.disabled){
19352                 this.setActiveItem(t, true);
19353             }
19354         }
19355         this.fireEvent("mouseover", this, e, t);
19356     },
19357
19358     // private
19359     onMouseOut : function(e){
19360         var t;
19361         if(t = this.findTargetItem(e)){
19362             if(t == this.activeItem && t.shouldDeactivate(e)){
19363                 this.activeItem.deactivate();
19364                 delete this.activeItem;
19365             }
19366         }
19367         this.fireEvent("mouseout", this, e, t);
19368     },
19369
19370     /**
19371      * Read-only.  Returns true if the menu is currently displayed, else false.
19372      * @type Boolean
19373      */
19374     isVisible : function(){
19375         return this.el && !this.hidden;
19376     },
19377
19378     /**
19379      * Displays this menu relative to another element
19380      * @param {String/HTMLElement/Roo.Element} element The element to align to
19381      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19382      * the element (defaults to this.defaultAlign)
19383      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19384      */
19385     show : function(el, pos, parentMenu){
19386         this.parentMenu = parentMenu;
19387         if(!this.el){
19388             this.render();
19389         }
19390         this.fireEvent("beforeshow", this);
19391         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19392     },
19393
19394     /**
19395      * Displays this menu at a specific xy position
19396      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19397      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19398      */
19399     showAt : function(xy, parentMenu, /* private: */_e){
19400         this.parentMenu = parentMenu;
19401         if(!this.el){
19402             this.render();
19403         }
19404         if(_e !== false){
19405             this.fireEvent("beforeshow", this);
19406             xy = this.el.adjustForConstraints(xy);
19407         }
19408         this.el.setXY(xy);
19409         this.el.show();
19410         this.hidden = false;
19411         this.focus();
19412         this.fireEvent("show", this);
19413     },
19414
19415     focus : function(){
19416         if(!this.hidden){
19417             this.doFocus.defer(50, this);
19418         }
19419     },
19420
19421     doFocus : function(){
19422         if(!this.hidden){
19423             this.focusEl.focus();
19424         }
19425     },
19426
19427     /**
19428      * Hides this menu and optionally all parent menus
19429      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19430      */
19431     hide : function(deep){
19432         if(this.el && this.isVisible()){
19433             this.fireEvent("beforehide", this);
19434             if(this.activeItem){
19435                 this.activeItem.deactivate();
19436                 this.activeItem = null;
19437             }
19438             this.el.hide();
19439             this.hidden = true;
19440             this.fireEvent("hide", this);
19441         }
19442         if(deep === true && this.parentMenu){
19443             this.parentMenu.hide(true);
19444         }
19445     },
19446
19447     /**
19448      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19449      * Any of the following are valid:
19450      * <ul>
19451      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19452      * <li>An HTMLElement object which will be converted to a menu item</li>
19453      * <li>A menu item config object that will be created as a new menu item</li>
19454      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19455      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19456      * </ul>
19457      * Usage:
19458      * <pre><code>
19459 // Create the menu
19460 var menu = new Roo.menu.Menu();
19461
19462 // Create a menu item to add by reference
19463 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19464
19465 // Add a bunch of items at once using different methods.
19466 // Only the last item added will be returned.
19467 var item = menu.add(
19468     menuItem,                // add existing item by ref
19469     'Dynamic Item',          // new TextItem
19470     '-',                     // new separator
19471     { text: 'Config Item' }  // new item by config
19472 );
19473 </code></pre>
19474      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19475      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19476      */
19477     add : function(){
19478         var a = arguments, l = a.length, item;
19479         for(var i = 0; i < l; i++){
19480             var el = a[i];
19481             if ((typeof(el) == "object") && el.xtype && el.xns) {
19482                 el = Roo.factory(el, Roo.menu);
19483             }
19484             
19485             if(el.render){ // some kind of Item
19486                 item = this.addItem(el);
19487             }else if(typeof el == "string"){ // string
19488                 if(el == "separator" || el == "-"){
19489                     item = this.addSeparator();
19490                 }else{
19491                     item = this.addText(el);
19492                 }
19493             }else if(el.tagName || el.el){ // element
19494                 item = this.addElement(el);
19495             }else if(typeof el == "object"){ // must be menu item config?
19496                 item = this.addMenuItem(el);
19497             }
19498         }
19499         return item;
19500     },
19501
19502     /**
19503      * Returns this menu's underlying {@link Roo.Element} object
19504      * @return {Roo.Element} The element
19505      */
19506     getEl : function(){
19507         if(!this.el){
19508             this.render();
19509         }
19510         return this.el;
19511     },
19512
19513     /**
19514      * Adds a separator bar to the menu
19515      * @return {Roo.menu.Item} The menu item that was added
19516      */
19517     addSeparator : function(){
19518         return this.addItem(new Roo.menu.Separator());
19519     },
19520
19521     /**
19522      * Adds an {@link Roo.Element} object to the menu
19523      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19524      * @return {Roo.menu.Item} The menu item that was added
19525      */
19526     addElement : function(el){
19527         return this.addItem(new Roo.menu.BaseItem(el));
19528     },
19529
19530     /**
19531      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19532      * @param {Roo.menu.Item} item The menu item to add
19533      * @return {Roo.menu.Item} The menu item that was added
19534      */
19535     addItem : function(item){
19536         this.items.add(item);
19537         if(this.ul){
19538             var li = document.createElement("li");
19539             li.className = "x-menu-list-item";
19540             this.ul.dom.appendChild(li);
19541             item.render(li, this);
19542             this.delayAutoWidth();
19543         }
19544         return item;
19545     },
19546
19547     /**
19548      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19549      * @param {Object} config A MenuItem config object
19550      * @return {Roo.menu.Item} The menu item that was added
19551      */
19552     addMenuItem : function(config){
19553         if(!(config instanceof Roo.menu.Item)){
19554             if(typeof config.checked == "boolean"){ // must be check menu item config?
19555                 config = new Roo.menu.CheckItem(config);
19556             }else{
19557                 config = new Roo.menu.Item(config);
19558             }
19559         }
19560         return this.addItem(config);
19561     },
19562
19563     /**
19564      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19565      * @param {String} text The text to display in the menu item
19566      * @return {Roo.menu.Item} The menu item that was added
19567      */
19568     addText : function(text){
19569         return this.addItem(new Roo.menu.TextItem({ text : text }));
19570     },
19571
19572     /**
19573      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19574      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19575      * @param {Roo.menu.Item} item The menu item to add
19576      * @return {Roo.menu.Item} The menu item that was added
19577      */
19578     insert : function(index, item){
19579         this.items.insert(index, item);
19580         if(this.ul){
19581             var li = document.createElement("li");
19582             li.className = "x-menu-list-item";
19583             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19584             item.render(li, this);
19585             this.delayAutoWidth();
19586         }
19587         return item;
19588     },
19589
19590     /**
19591      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19592      * @param {Roo.menu.Item} item The menu item to remove
19593      */
19594     remove : function(item){
19595         this.items.removeKey(item.id);
19596         item.destroy();
19597     },
19598
19599     /**
19600      * Removes and destroys all items in the menu
19601      */
19602     removeAll : function(){
19603         var f;
19604         while(f = this.items.first()){
19605             this.remove(f);
19606         }
19607     }
19608 });
19609
19610 // MenuNav is a private utility class used internally by the Menu
19611 Roo.menu.MenuNav = function(menu){
19612     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19613     this.scope = this.menu = menu;
19614 };
19615
19616 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19617     doRelay : function(e, h){
19618         var k = e.getKey();
19619         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19620             this.menu.tryActivate(0, 1);
19621             return false;
19622         }
19623         return h.call(this.scope || this, e, this.menu);
19624     },
19625
19626     up : function(e, m){
19627         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19628             m.tryActivate(m.items.length-1, -1);
19629         }
19630     },
19631
19632     down : function(e, m){
19633         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19634             m.tryActivate(0, 1);
19635         }
19636     },
19637
19638     right : function(e, m){
19639         if(m.activeItem){
19640             m.activeItem.expandMenu(true);
19641         }
19642     },
19643
19644     left : function(e, m){
19645         m.hide();
19646         if(m.parentMenu && m.parentMenu.activeItem){
19647             m.parentMenu.activeItem.activate();
19648         }
19649     },
19650
19651     enter : function(e, m){
19652         if(m.activeItem){
19653             e.stopPropagation();
19654             m.activeItem.onClick(e);
19655             m.fireEvent("click", this, m.activeItem);
19656             return true;
19657         }
19658     }
19659 });/*
19660  * Based on:
19661  * Ext JS Library 1.1.1
19662  * Copyright(c) 2006-2007, Ext JS, LLC.
19663  *
19664  * Originally Released Under LGPL - original licence link has changed is not relivant.
19665  *
19666  * Fork - LGPL
19667  * <script type="text/javascript">
19668  */
19669  
19670 /**
19671  * @class Roo.menu.MenuMgr
19672  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19673  * @singleton
19674  */
19675 Roo.menu.MenuMgr = function(){
19676    var menus, active, groups = {}, attached = false, lastShow = new Date();
19677
19678    // private - called when first menu is created
19679    function init(){
19680        menus = {};
19681        active = new Roo.util.MixedCollection();
19682        Roo.get(document).addKeyListener(27, function(){
19683            if(active.length > 0){
19684                hideAll();
19685            }
19686        });
19687    }
19688
19689    // private
19690    function hideAll(){
19691        if(active && active.length > 0){
19692            var c = active.clone();
19693            c.each(function(m){
19694                m.hide();
19695            });
19696        }
19697    }
19698
19699    // private
19700    function onHide(m){
19701        active.remove(m);
19702        if(active.length < 1){
19703            Roo.get(document).un("mousedown", onMouseDown);
19704            attached = false;
19705        }
19706    }
19707
19708    // private
19709    function onShow(m){
19710        var last = active.last();
19711        lastShow = new Date();
19712        active.add(m);
19713        if(!attached){
19714            Roo.get(document).on("mousedown", onMouseDown);
19715            attached = true;
19716        }
19717        if(m.parentMenu){
19718           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19719           m.parentMenu.activeChild = m;
19720        }else if(last && last.isVisible()){
19721           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19722        }
19723    }
19724
19725    // private
19726    function onBeforeHide(m){
19727        if(m.activeChild){
19728            m.activeChild.hide();
19729        }
19730        if(m.autoHideTimer){
19731            clearTimeout(m.autoHideTimer);
19732            delete m.autoHideTimer;
19733        }
19734    }
19735
19736    // private
19737    function onBeforeShow(m){
19738        var pm = m.parentMenu;
19739        if(!pm && !m.allowOtherMenus){
19740            hideAll();
19741        }else if(pm && pm.activeChild && active != m){
19742            pm.activeChild.hide();
19743        }
19744    }
19745
19746    // private
19747    function onMouseDown(e){
19748        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19749            hideAll();
19750        }
19751    }
19752
19753    // private
19754    function onBeforeCheck(mi, state){
19755        if(state){
19756            var g = groups[mi.group];
19757            for(var i = 0, l = g.length; i < l; i++){
19758                if(g[i] != mi){
19759                    g[i].setChecked(false);
19760                }
19761            }
19762        }
19763    }
19764
19765    return {
19766
19767        /**
19768         * Hides all menus that are currently visible
19769         */
19770        hideAll : function(){
19771             hideAll();  
19772        },
19773
19774        // private
19775        register : function(menu){
19776            if(!menus){
19777                init();
19778            }
19779            menus[menu.id] = menu;
19780            menu.on("beforehide", onBeforeHide);
19781            menu.on("hide", onHide);
19782            menu.on("beforeshow", onBeforeShow);
19783            menu.on("show", onShow);
19784            var g = menu.group;
19785            if(g && menu.events["checkchange"]){
19786                if(!groups[g]){
19787                    groups[g] = [];
19788                }
19789                groups[g].push(menu);
19790                menu.on("checkchange", onCheck);
19791            }
19792        },
19793
19794         /**
19795          * Returns a {@link Roo.menu.Menu} object
19796          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19797          * be used to generate and return a new Menu instance.
19798          */
19799        get : function(menu){
19800            if(typeof menu == "string"){ // menu id
19801                return menus[menu];
19802            }else if(menu.events){  // menu instance
19803                return menu;
19804            }else if(typeof menu.length == 'number'){ // array of menu items?
19805                return new Roo.menu.Menu({items:menu});
19806            }else{ // otherwise, must be a config
19807                return new Roo.menu.Menu(menu);
19808            }
19809        },
19810
19811        // private
19812        unregister : function(menu){
19813            delete menus[menu.id];
19814            menu.un("beforehide", onBeforeHide);
19815            menu.un("hide", onHide);
19816            menu.un("beforeshow", onBeforeShow);
19817            menu.un("show", onShow);
19818            var g = menu.group;
19819            if(g && menu.events["checkchange"]){
19820                groups[g].remove(menu);
19821                menu.un("checkchange", onCheck);
19822            }
19823        },
19824
19825        // private
19826        registerCheckable : function(menuItem){
19827            var g = menuItem.group;
19828            if(g){
19829                if(!groups[g]){
19830                    groups[g] = [];
19831                }
19832                groups[g].push(menuItem);
19833                menuItem.on("beforecheckchange", onBeforeCheck);
19834            }
19835        },
19836
19837        // private
19838        unregisterCheckable : function(menuItem){
19839            var g = menuItem.group;
19840            if(g){
19841                groups[g].remove(menuItem);
19842                menuItem.un("beforecheckchange", onBeforeCheck);
19843            }
19844        }
19845    };
19846 }();/*
19847  * Based on:
19848  * Ext JS Library 1.1.1
19849  * Copyright(c) 2006-2007, Ext JS, LLC.
19850  *
19851  * Originally Released Under LGPL - original licence link has changed is not relivant.
19852  *
19853  * Fork - LGPL
19854  * <script type="text/javascript">
19855  */
19856  
19857
19858 /**
19859  * @class Roo.menu.BaseItem
19860  * @extends Roo.Component
19861  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19862  * management and base configuration options shared by all menu components.
19863  * @constructor
19864  * Creates a new BaseItem
19865  * @param {Object} config Configuration options
19866  */
19867 Roo.menu.BaseItem = function(config){
19868     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19869
19870     this.addEvents({
19871         /**
19872          * @event click
19873          * Fires when this item is clicked
19874          * @param {Roo.menu.BaseItem} this
19875          * @param {Roo.EventObject} e
19876          */
19877         click: true,
19878         /**
19879          * @event activate
19880          * Fires when this item is activated
19881          * @param {Roo.menu.BaseItem} this
19882          */
19883         activate : true,
19884         /**
19885          * @event deactivate
19886          * Fires when this item is deactivated
19887          * @param {Roo.menu.BaseItem} this
19888          */
19889         deactivate : true
19890     });
19891
19892     if(this.handler){
19893         this.on("click", this.handler, this.scope, true);
19894     }
19895 };
19896
19897 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19898     /**
19899      * @cfg {Function} handler
19900      * A function that will handle the click event of this menu item (defaults to undefined)
19901      */
19902     /**
19903      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19904      */
19905     canActivate : false,
19906     
19907      /**
19908      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19909      */
19910     hidden: false,
19911     
19912     /**
19913      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19914      */
19915     activeClass : "x-menu-item-active",
19916     /**
19917      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19918      */
19919     hideOnClick : true,
19920     /**
19921      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19922      */
19923     hideDelay : 100,
19924
19925     // private
19926     ctype: "Roo.menu.BaseItem",
19927
19928     // private
19929     actionMode : "container",
19930
19931     // private
19932     render : function(container, parentMenu){
19933         this.parentMenu = parentMenu;
19934         Roo.menu.BaseItem.superclass.render.call(this, container);
19935         this.container.menuItemId = this.id;
19936     },
19937
19938     // private
19939     onRender : function(container, position){
19940         this.el = Roo.get(this.el);
19941         container.dom.appendChild(this.el.dom);
19942     },
19943
19944     // private
19945     onClick : function(e){
19946         if(!this.disabled && this.fireEvent("click", this, e) !== false
19947                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19948             this.handleClick(e);
19949         }else{
19950             e.stopEvent();
19951         }
19952     },
19953
19954     // private
19955     activate : function(){
19956         if(this.disabled){
19957             return false;
19958         }
19959         var li = this.container;
19960         li.addClass(this.activeClass);
19961         this.region = li.getRegion().adjust(2, 2, -2, -2);
19962         this.fireEvent("activate", this);
19963         return true;
19964     },
19965
19966     // private
19967     deactivate : function(){
19968         this.container.removeClass(this.activeClass);
19969         this.fireEvent("deactivate", this);
19970     },
19971
19972     // private
19973     shouldDeactivate : function(e){
19974         return !this.region || !this.region.contains(e.getPoint());
19975     },
19976
19977     // private
19978     handleClick : function(e){
19979         if(this.hideOnClick){
19980             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19981         }
19982     },
19983
19984     // private
19985     expandMenu : function(autoActivate){
19986         // do nothing
19987     },
19988
19989     // private
19990     hideMenu : function(){
19991         // do nothing
19992     }
19993 });/*
19994  * Based on:
19995  * Ext JS Library 1.1.1
19996  * Copyright(c) 2006-2007, Ext JS, LLC.
19997  *
19998  * Originally Released Under LGPL - original licence link has changed is not relivant.
19999  *
20000  * Fork - LGPL
20001  * <script type="text/javascript">
20002  */
20003  
20004 /**
20005  * @class Roo.menu.Adapter
20006  * @extends Roo.menu.BaseItem
20007  * 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.
20008  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20009  * @constructor
20010  * Creates a new Adapter
20011  * @param {Object} config Configuration options
20012  */
20013 Roo.menu.Adapter = function(component, config){
20014     Roo.menu.Adapter.superclass.constructor.call(this, config);
20015     this.component = component;
20016 };
20017 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20018     // private
20019     canActivate : true,
20020
20021     // private
20022     onRender : function(container, position){
20023         this.component.render(container);
20024         this.el = this.component.getEl();
20025     },
20026
20027     // private
20028     activate : function(){
20029         if(this.disabled){
20030             return false;
20031         }
20032         this.component.focus();
20033         this.fireEvent("activate", this);
20034         return true;
20035     },
20036
20037     // private
20038     deactivate : function(){
20039         this.fireEvent("deactivate", this);
20040     },
20041
20042     // private
20043     disable : function(){
20044         this.component.disable();
20045         Roo.menu.Adapter.superclass.disable.call(this);
20046     },
20047
20048     // private
20049     enable : function(){
20050         this.component.enable();
20051         Roo.menu.Adapter.superclass.enable.call(this);
20052     }
20053 });/*
20054  * Based on:
20055  * Ext JS Library 1.1.1
20056  * Copyright(c) 2006-2007, Ext JS, LLC.
20057  *
20058  * Originally Released Under LGPL - original licence link has changed is not relivant.
20059  *
20060  * Fork - LGPL
20061  * <script type="text/javascript">
20062  */
20063
20064 /**
20065  * @class Roo.menu.TextItem
20066  * @extends Roo.menu.BaseItem
20067  * Adds a static text string to a menu, usually used as either a heading or group separator.
20068  * Note: old style constructor with text is still supported.
20069  * 
20070  * @constructor
20071  * Creates a new TextItem
20072  * @param {Object} cfg Configuration
20073  */
20074 Roo.menu.TextItem = function(cfg){
20075     if (typeof(cfg) == 'string') {
20076         this.text = cfg;
20077     } else {
20078         Roo.apply(this,cfg);
20079     }
20080     
20081     Roo.menu.TextItem.superclass.constructor.call(this);
20082 };
20083
20084 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20085     /**
20086      * @cfg {Boolean} text Text to show on item.
20087      */
20088     text : '',
20089     
20090     /**
20091      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20092      */
20093     hideOnClick : false,
20094     /**
20095      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20096      */
20097     itemCls : "x-menu-text",
20098
20099     // private
20100     onRender : function(){
20101         var s = document.createElement("span");
20102         s.className = this.itemCls;
20103         s.innerHTML = this.text;
20104         this.el = s;
20105         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20106     }
20107 });/*
20108  * Based on:
20109  * Ext JS Library 1.1.1
20110  * Copyright(c) 2006-2007, Ext JS, LLC.
20111  *
20112  * Originally Released Under LGPL - original licence link has changed is not relivant.
20113  *
20114  * Fork - LGPL
20115  * <script type="text/javascript">
20116  */
20117
20118 /**
20119  * @class Roo.menu.Separator
20120  * @extends Roo.menu.BaseItem
20121  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20122  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20123  * @constructor
20124  * @param {Object} config Configuration options
20125  */
20126 Roo.menu.Separator = function(config){
20127     Roo.menu.Separator.superclass.constructor.call(this, config);
20128 };
20129
20130 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20131     /**
20132      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20133      */
20134     itemCls : "x-menu-sep",
20135     /**
20136      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20137      */
20138     hideOnClick : false,
20139
20140     // private
20141     onRender : function(li){
20142         var s = document.createElement("span");
20143         s.className = this.itemCls;
20144         s.innerHTML = "&#160;";
20145         this.el = s;
20146         li.addClass("x-menu-sep-li");
20147         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20148     }
20149 });/*
20150  * Based on:
20151  * Ext JS Library 1.1.1
20152  * Copyright(c) 2006-2007, Ext JS, LLC.
20153  *
20154  * Originally Released Under LGPL - original licence link has changed is not relivant.
20155  *
20156  * Fork - LGPL
20157  * <script type="text/javascript">
20158  */
20159 /**
20160  * @class Roo.menu.Item
20161  * @extends Roo.menu.BaseItem
20162  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20163  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20164  * activation and click handling.
20165  * @constructor
20166  * Creates a new Item
20167  * @param {Object} config Configuration options
20168  */
20169 Roo.menu.Item = function(config){
20170     Roo.menu.Item.superclass.constructor.call(this, config);
20171     if(this.menu){
20172         this.menu = Roo.menu.MenuMgr.get(this.menu);
20173     }
20174 };
20175 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20176     
20177     /**
20178      * @cfg {String} text
20179      * The text to show on the menu item.
20180      */
20181     text: '',
20182      /**
20183      * @cfg {String} HTML to render in menu
20184      * The text to show on the menu item (HTML version).
20185      */
20186     html: '',
20187     /**
20188      * @cfg {String} icon
20189      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20190      */
20191     icon: undefined,
20192     /**
20193      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20194      */
20195     itemCls : "x-menu-item",
20196     /**
20197      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20198      */
20199     canActivate : true,
20200     /**
20201      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20202      */
20203     showDelay: 200,
20204     // doc'd in BaseItem
20205     hideDelay: 200,
20206
20207     // private
20208     ctype: "Roo.menu.Item",
20209     
20210     // private
20211     onRender : function(container, position){
20212         var el = document.createElement("a");
20213         el.hideFocus = true;
20214         el.unselectable = "on";
20215         el.href = this.href || "#";
20216         if(this.hrefTarget){
20217             el.target = this.hrefTarget;
20218         }
20219         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20220         
20221         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20222         
20223         el.innerHTML = String.format(
20224                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20225                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20226         this.el = el;
20227         Roo.menu.Item.superclass.onRender.call(this, container, position);
20228     },
20229
20230     /**
20231      * Sets the text to display in this menu item
20232      * @param {String} text The text to display
20233      * @param {Boolean} isHTML true to indicate text is pure html.
20234      */
20235     setText : function(text, isHTML){
20236         if (isHTML) {
20237             this.html = text;
20238         } else {
20239             this.text = text;
20240             this.html = '';
20241         }
20242         if(this.rendered){
20243             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20244      
20245             this.el.update(String.format(
20246                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20247                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20248             this.parentMenu.autoWidth();
20249         }
20250     },
20251
20252     // private
20253     handleClick : function(e){
20254         if(!this.href){ // if no link defined, stop the event automatically
20255             e.stopEvent();
20256         }
20257         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20258     },
20259
20260     // private
20261     activate : function(autoExpand){
20262         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20263             this.focus();
20264             if(autoExpand){
20265                 this.expandMenu();
20266             }
20267         }
20268         return true;
20269     },
20270
20271     // private
20272     shouldDeactivate : function(e){
20273         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20274             if(this.menu && this.menu.isVisible()){
20275                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20276             }
20277             return true;
20278         }
20279         return false;
20280     },
20281
20282     // private
20283     deactivate : function(){
20284         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20285         this.hideMenu();
20286     },
20287
20288     // private
20289     expandMenu : function(autoActivate){
20290         if(!this.disabled && this.menu){
20291             clearTimeout(this.hideTimer);
20292             delete this.hideTimer;
20293             if(!this.menu.isVisible() && !this.showTimer){
20294                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20295             }else if (this.menu.isVisible() && autoActivate){
20296                 this.menu.tryActivate(0, 1);
20297             }
20298         }
20299     },
20300
20301     // private
20302     deferExpand : function(autoActivate){
20303         delete this.showTimer;
20304         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20305         if(autoActivate){
20306             this.menu.tryActivate(0, 1);
20307         }
20308     },
20309
20310     // private
20311     hideMenu : function(){
20312         clearTimeout(this.showTimer);
20313         delete this.showTimer;
20314         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20315             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20316         }
20317     },
20318
20319     // private
20320     deferHide : function(){
20321         delete this.hideTimer;
20322         this.menu.hide();
20323     }
20324 });/*
20325  * Based on:
20326  * Ext JS Library 1.1.1
20327  * Copyright(c) 2006-2007, Ext JS, LLC.
20328  *
20329  * Originally Released Under LGPL - original licence link has changed is not relivant.
20330  *
20331  * Fork - LGPL
20332  * <script type="text/javascript">
20333  */
20334  
20335 /**
20336  * @class Roo.menu.CheckItem
20337  * @extends Roo.menu.Item
20338  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20339  * @constructor
20340  * Creates a new CheckItem
20341  * @param {Object} config Configuration options
20342  */
20343 Roo.menu.CheckItem = function(config){
20344     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20345     this.addEvents({
20346         /**
20347          * @event beforecheckchange
20348          * Fires before the checked value is set, providing an opportunity to cancel if needed
20349          * @param {Roo.menu.CheckItem} this
20350          * @param {Boolean} checked The new checked value that will be set
20351          */
20352         "beforecheckchange" : true,
20353         /**
20354          * @event checkchange
20355          * Fires after the checked value has been set
20356          * @param {Roo.menu.CheckItem} this
20357          * @param {Boolean} checked The checked value that was set
20358          */
20359         "checkchange" : true
20360     });
20361     if(this.checkHandler){
20362         this.on('checkchange', this.checkHandler, this.scope);
20363     }
20364 };
20365 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20366     /**
20367      * @cfg {String} group
20368      * All check items with the same group name will automatically be grouped into a single-select
20369      * radio button group (defaults to '')
20370      */
20371     /**
20372      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20373      */
20374     itemCls : "x-menu-item x-menu-check-item",
20375     /**
20376      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20377      */
20378     groupClass : "x-menu-group-item",
20379
20380     /**
20381      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20382      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20383      * initialized with checked = true will be rendered as checked.
20384      */
20385     checked: false,
20386
20387     // private
20388     ctype: "Roo.menu.CheckItem",
20389
20390     // private
20391     onRender : function(c){
20392         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20393         if(this.group){
20394             this.el.addClass(this.groupClass);
20395         }
20396         Roo.menu.MenuMgr.registerCheckable(this);
20397         if(this.checked){
20398             this.checked = false;
20399             this.setChecked(true, true);
20400         }
20401     },
20402
20403     // private
20404     destroy : function(){
20405         if(this.rendered){
20406             Roo.menu.MenuMgr.unregisterCheckable(this);
20407         }
20408         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20409     },
20410
20411     /**
20412      * Set the checked state of this item
20413      * @param {Boolean} checked The new checked value
20414      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20415      */
20416     setChecked : function(state, suppressEvent){
20417         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20418             if(this.container){
20419                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20420             }
20421             this.checked = state;
20422             if(suppressEvent !== true){
20423                 this.fireEvent("checkchange", this, state);
20424             }
20425         }
20426     },
20427
20428     // private
20429     handleClick : function(e){
20430        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20431            this.setChecked(!this.checked);
20432        }
20433        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20434     }
20435 });/*
20436  * Based on:
20437  * Ext JS Library 1.1.1
20438  * Copyright(c) 2006-2007, Ext JS, LLC.
20439  *
20440  * Originally Released Under LGPL - original licence link has changed is not relivant.
20441  *
20442  * Fork - LGPL
20443  * <script type="text/javascript">
20444  */
20445  
20446 /**
20447  * @class Roo.menu.DateItem
20448  * @extends Roo.menu.Adapter
20449  * A menu item that wraps the {@link Roo.DatPicker} component.
20450  * @constructor
20451  * Creates a new DateItem
20452  * @param {Object} config Configuration options
20453  */
20454 Roo.menu.DateItem = function(config){
20455     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20456     /** The Roo.DatePicker object @type Roo.DatePicker */
20457     this.picker = this.component;
20458     this.addEvents({select: true});
20459     
20460     this.picker.on("render", function(picker){
20461         picker.getEl().swallowEvent("click");
20462         picker.container.addClass("x-menu-date-item");
20463     });
20464
20465     this.picker.on("select", this.onSelect, this);
20466 };
20467
20468 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20469     // private
20470     onSelect : function(picker, date){
20471         this.fireEvent("select", this, date, picker);
20472         Roo.menu.DateItem.superclass.handleClick.call(this);
20473     }
20474 });/*
20475  * Based on:
20476  * Ext JS Library 1.1.1
20477  * Copyright(c) 2006-2007, Ext JS, LLC.
20478  *
20479  * Originally Released Under LGPL - original licence link has changed is not relivant.
20480  *
20481  * Fork - LGPL
20482  * <script type="text/javascript">
20483  */
20484  
20485 /**
20486  * @class Roo.menu.ColorItem
20487  * @extends Roo.menu.Adapter
20488  * A menu item that wraps the {@link Roo.ColorPalette} component.
20489  * @constructor
20490  * Creates a new ColorItem
20491  * @param {Object} config Configuration options
20492  */
20493 Roo.menu.ColorItem = function(config){
20494     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20495     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20496     this.palette = this.component;
20497     this.relayEvents(this.palette, ["select"]);
20498     if(this.selectHandler){
20499         this.on('select', this.selectHandler, this.scope);
20500     }
20501 };
20502 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20503  * Based on:
20504  * Ext JS Library 1.1.1
20505  * Copyright(c) 2006-2007, Ext JS, LLC.
20506  *
20507  * Originally Released Under LGPL - original licence link has changed is not relivant.
20508  *
20509  * Fork - LGPL
20510  * <script type="text/javascript">
20511  */
20512  
20513
20514 /**
20515  * @class Roo.menu.DateMenu
20516  * @extends Roo.menu.Menu
20517  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20518  * @constructor
20519  * Creates a new DateMenu
20520  * @param {Object} config Configuration options
20521  */
20522 Roo.menu.DateMenu = function(config){
20523     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20524     this.plain = true;
20525     var di = new Roo.menu.DateItem(config);
20526     this.add(di);
20527     /**
20528      * The {@link Roo.DatePicker} instance for this DateMenu
20529      * @type DatePicker
20530      */
20531     this.picker = di.picker;
20532     /**
20533      * @event select
20534      * @param {DatePicker} picker
20535      * @param {Date} date
20536      */
20537     this.relayEvents(di, ["select"]);
20538     this.on('beforeshow', function(){
20539         if(this.picker){
20540             this.picker.hideMonthPicker(false);
20541         }
20542     }, this);
20543 };
20544 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20545     cls:'x-date-menu'
20546 });/*
20547  * Based on:
20548  * Ext JS Library 1.1.1
20549  * Copyright(c) 2006-2007, Ext JS, LLC.
20550  *
20551  * Originally Released Under LGPL - original licence link has changed is not relivant.
20552  *
20553  * Fork - LGPL
20554  * <script type="text/javascript">
20555  */
20556  
20557
20558 /**
20559  * @class Roo.menu.ColorMenu
20560  * @extends Roo.menu.Menu
20561  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20562  * @constructor
20563  * Creates a new ColorMenu
20564  * @param {Object} config Configuration options
20565  */
20566 Roo.menu.ColorMenu = function(config){
20567     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20568     this.plain = true;
20569     var ci = new Roo.menu.ColorItem(config);
20570     this.add(ci);
20571     /**
20572      * The {@link Roo.ColorPalette} instance for this ColorMenu
20573      * @type ColorPalette
20574      */
20575     this.palette = ci.palette;
20576     /**
20577      * @event select
20578      * @param {ColorPalette} palette
20579      * @param {String} color
20580      */
20581     this.relayEvents(ci, ["select"]);
20582 };
20583 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20584  * Based on:
20585  * Ext JS Library 1.1.1
20586  * Copyright(c) 2006-2007, Ext JS, LLC.
20587  *
20588  * Originally Released Under LGPL - original licence link has changed is not relivant.
20589  *
20590  * Fork - LGPL
20591  * <script type="text/javascript">
20592  */
20593  
20594 /**
20595  * @class Roo.form.Field
20596  * @extends Roo.BoxComponent
20597  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20598  * @constructor
20599  * Creates a new Field
20600  * @param {Object} config Configuration options
20601  */
20602 Roo.form.Field = function(config){
20603     Roo.form.Field.superclass.constructor.call(this, config);
20604 };
20605
20606 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20607     /**
20608      * @cfg {String} fieldLabel Label to use when rendering a form.
20609      */
20610        /**
20611      * @cfg {String} qtip Mouse over tip
20612      */
20613      
20614     /**
20615      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20616      */
20617     invalidClass : "x-form-invalid",
20618     /**
20619      * @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")
20620      */
20621     invalidText : "The value in this field is invalid",
20622     /**
20623      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20624      */
20625     focusClass : "x-form-focus",
20626     /**
20627      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20628       automatic validation (defaults to "keyup").
20629      */
20630     validationEvent : "keyup",
20631     /**
20632      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20633      */
20634     validateOnBlur : true,
20635     /**
20636      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20637      */
20638     validationDelay : 250,
20639     /**
20640      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20641      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20642      */
20643     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20644     /**
20645      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20646      */
20647     fieldClass : "x-form-field",
20648     /**
20649      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20650      *<pre>
20651 Value         Description
20652 -----------   ----------------------------------------------------------------------
20653 qtip          Display a quick tip when the user hovers over the field
20654 title         Display a default browser title attribute popup
20655 under         Add a block div beneath the field containing the error text
20656 side          Add an error icon to the right of the field with a popup on hover
20657 [element id]  Add the error text directly to the innerHTML of the specified element
20658 </pre>
20659      */
20660     msgTarget : 'qtip',
20661     /**
20662      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20663      */
20664     msgFx : 'normal',
20665
20666     /**
20667      * @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.
20668      */
20669     readOnly : false,
20670
20671     /**
20672      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20673      */
20674     disabled : false,
20675
20676     /**
20677      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20678      */
20679     inputType : undefined,
20680     
20681     /**
20682      * @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).
20683          */
20684         tabIndex : undefined,
20685         
20686     // private
20687     isFormField : true,
20688
20689     // private
20690     hasFocus : false,
20691     /**
20692      * @property {Roo.Element} fieldEl
20693      * Element Containing the rendered Field (with label etc.)
20694      */
20695     /**
20696      * @cfg {Mixed} value A value to initialize this field with.
20697      */
20698     value : undefined,
20699
20700     /**
20701      * @cfg {String} name The field's HTML name attribute.
20702      */
20703     /**
20704      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20705      */
20706
20707         // private ??
20708         initComponent : function(){
20709         Roo.form.Field.superclass.initComponent.call(this);
20710         this.addEvents({
20711             /**
20712              * @event focus
20713              * Fires when this field receives input focus.
20714              * @param {Roo.form.Field} this
20715              */
20716             focus : true,
20717             /**
20718              * @event blur
20719              * Fires when this field loses input focus.
20720              * @param {Roo.form.Field} this
20721              */
20722             blur : true,
20723             /**
20724              * @event specialkey
20725              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20726              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20727              * @param {Roo.form.Field} this
20728              * @param {Roo.EventObject} e The event object
20729              */
20730             specialkey : true,
20731             /**
20732              * @event change
20733              * Fires just before the field blurs if the field value has changed.
20734              * @param {Roo.form.Field} this
20735              * @param {Mixed} newValue The new value
20736              * @param {Mixed} oldValue The original value
20737              */
20738             change : true,
20739             /**
20740              * @event invalid
20741              * Fires after the field has been marked as invalid.
20742              * @param {Roo.form.Field} this
20743              * @param {String} msg The validation message
20744              */
20745             invalid : true,
20746             /**
20747              * @event valid
20748              * Fires after the field has been validated with no errors.
20749              * @param {Roo.form.Field} this
20750              */
20751             valid : true,
20752              /**
20753              * @event keyup
20754              * Fires after the key up
20755              * @param {Roo.form.Field} this
20756              * @param {Roo.EventObject}  e The event Object
20757              */
20758             keyup : true
20759         });
20760     },
20761
20762     /**
20763      * Returns the name attribute of the field if available
20764      * @return {String} name The field name
20765      */
20766     getName: function(){
20767          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20768     },
20769
20770     // private
20771     onRender : function(ct, position){
20772         Roo.form.Field.superclass.onRender.call(this, ct, position);
20773         if(!this.el){
20774             var cfg = this.getAutoCreate();
20775             if(!cfg.name){
20776                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20777             }
20778             if (!cfg.name.length) {
20779                 delete cfg.name;
20780             }
20781             if(this.inputType){
20782                 cfg.type = this.inputType;
20783             }
20784             this.el = ct.createChild(cfg, position);
20785         }
20786         var type = this.el.dom.type;
20787         if(type){
20788             if(type == 'password'){
20789                 type = 'text';
20790             }
20791             this.el.addClass('x-form-'+type);
20792         }
20793         if(this.readOnly){
20794             this.el.dom.readOnly = true;
20795         }
20796         if(this.tabIndex !== undefined){
20797             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20798         }
20799
20800         this.el.addClass([this.fieldClass, this.cls]);
20801         this.initValue();
20802     },
20803
20804     /**
20805      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20806      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20807      * @return {Roo.form.Field} this
20808      */
20809     applyTo : function(target){
20810         this.allowDomMove = false;
20811         this.el = Roo.get(target);
20812         this.render(this.el.dom.parentNode);
20813         return this;
20814     },
20815
20816     // private
20817     initValue : function(){
20818         if(this.value !== undefined){
20819             this.setValue(this.value);
20820         }else if(this.el.dom.value.length > 0){
20821             this.setValue(this.el.dom.value);
20822         }
20823     },
20824
20825     /**
20826      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20827      */
20828     isDirty : function() {
20829         if(this.disabled) {
20830             return false;
20831         }
20832         return String(this.getValue()) !== String(this.originalValue);
20833     },
20834
20835     // private
20836     afterRender : function(){
20837         Roo.form.Field.superclass.afterRender.call(this);
20838         this.initEvents();
20839     },
20840
20841     // private
20842     fireKey : function(e){
20843         //Roo.log('field ' + e.getKey());
20844         if(e.isNavKeyPress()){
20845             this.fireEvent("specialkey", this, e);
20846         }
20847     },
20848
20849     /**
20850      * Resets the current field value to the originally loaded value and clears any validation messages
20851      */
20852     reset : function(){
20853         this.setValue(this.resetValue);
20854         this.clearInvalid();
20855     },
20856
20857     // private
20858     initEvents : function(){
20859         // safari killled keypress - so keydown is now used..
20860         this.el.on("keydown" , this.fireKey,  this);
20861         this.el.on("focus", this.onFocus,  this);
20862         this.el.on("blur", this.onBlur,  this);
20863         this.el.relayEvent('keyup', this);
20864
20865         // reference to original value for reset
20866         this.originalValue = this.getValue();
20867         this.resetValue =  this.getValue();
20868     },
20869
20870     // private
20871     onFocus : function(){
20872         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20873             this.el.addClass(this.focusClass);
20874         }
20875         if(!this.hasFocus){
20876             this.hasFocus = true;
20877             this.startValue = this.getValue();
20878             this.fireEvent("focus", this);
20879         }
20880     },
20881
20882     beforeBlur : Roo.emptyFn,
20883
20884     // private
20885     onBlur : function(){
20886         this.beforeBlur();
20887         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20888             this.el.removeClass(this.focusClass);
20889         }
20890         this.hasFocus = false;
20891         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20892             this.validate();
20893         }
20894         var v = this.getValue();
20895         if(String(v) !== String(this.startValue)){
20896             this.fireEvent('change', this, v, this.startValue);
20897         }
20898         this.fireEvent("blur", this);
20899     },
20900
20901     /**
20902      * Returns whether or not the field value is currently valid
20903      * @param {Boolean} preventMark True to disable marking the field invalid
20904      * @return {Boolean} True if the value is valid, else false
20905      */
20906     isValid : function(preventMark){
20907         if(this.disabled){
20908             return true;
20909         }
20910         var restore = this.preventMark;
20911         this.preventMark = preventMark === true;
20912         var v = this.validateValue(this.processValue(this.getRawValue()));
20913         this.preventMark = restore;
20914         return v;
20915     },
20916
20917     /**
20918      * Validates the field value
20919      * @return {Boolean} True if the value is valid, else false
20920      */
20921     validate : function(){
20922         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20923             this.clearInvalid();
20924             return true;
20925         }
20926         return false;
20927     },
20928
20929     processValue : function(value){
20930         return value;
20931     },
20932
20933     // private
20934     // Subclasses should provide the validation implementation by overriding this
20935     validateValue : function(value){
20936         return true;
20937     },
20938
20939     /**
20940      * Mark this field as invalid
20941      * @param {String} msg The validation message
20942      */
20943     markInvalid : function(msg){
20944         if(!this.rendered || this.preventMark){ // not rendered
20945             return;
20946         }
20947         
20948         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20949         
20950         obj.el.addClass(this.invalidClass);
20951         msg = msg || this.invalidText;
20952         switch(this.msgTarget){
20953             case 'qtip':
20954                 obj.el.dom.qtip = msg;
20955                 obj.el.dom.qclass = 'x-form-invalid-tip';
20956                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20957                     Roo.QuickTips.enable();
20958                 }
20959                 break;
20960             case 'title':
20961                 this.el.dom.title = msg;
20962                 break;
20963             case 'under':
20964                 if(!this.errorEl){
20965                     var elp = this.el.findParent('.x-form-element', 5, true);
20966                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20967                     this.errorEl.setWidth(elp.getWidth(true)-20);
20968                 }
20969                 this.errorEl.update(msg);
20970                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20971                 break;
20972             case 'side':
20973                 if(!this.errorIcon){
20974                     var elp = this.el.findParent('.x-form-element', 5, true);
20975                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20976                 }
20977                 this.alignErrorIcon();
20978                 this.errorIcon.dom.qtip = msg;
20979                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20980                 this.errorIcon.show();
20981                 this.on('resize', this.alignErrorIcon, this);
20982                 break;
20983             default:
20984                 var t = Roo.getDom(this.msgTarget);
20985                 t.innerHTML = msg;
20986                 t.style.display = this.msgDisplay;
20987                 break;
20988         }
20989         this.fireEvent('invalid', this, msg);
20990     },
20991
20992     // private
20993     alignErrorIcon : function(){
20994         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20995     },
20996
20997     /**
20998      * Clear any invalid styles/messages for this field
20999      */
21000     clearInvalid : function(){
21001         if(!this.rendered || this.preventMark){ // not rendered
21002             return;
21003         }
21004         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21005         
21006         obj.el.removeClass(this.invalidClass);
21007         switch(this.msgTarget){
21008             case 'qtip':
21009                 obj.el.dom.qtip = '';
21010                 break;
21011             case 'title':
21012                 this.el.dom.title = '';
21013                 break;
21014             case 'under':
21015                 if(this.errorEl){
21016                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21017                 }
21018                 break;
21019             case 'side':
21020                 if(this.errorIcon){
21021                     this.errorIcon.dom.qtip = '';
21022                     this.errorIcon.hide();
21023                     this.un('resize', this.alignErrorIcon, this);
21024                 }
21025                 break;
21026             default:
21027                 var t = Roo.getDom(this.msgTarget);
21028                 t.innerHTML = '';
21029                 t.style.display = 'none';
21030                 break;
21031         }
21032         this.fireEvent('valid', this);
21033     },
21034
21035     /**
21036      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21037      * @return {Mixed} value The field value
21038      */
21039     getRawValue : function(){
21040         var v = this.el.getValue();
21041         
21042         return v;
21043     },
21044
21045     /**
21046      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21047      * @return {Mixed} value The field value
21048      */
21049     getValue : function(){
21050         var v = this.el.getValue();
21051          
21052         return v;
21053     },
21054
21055     /**
21056      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21057      * @param {Mixed} value The value to set
21058      */
21059     setRawValue : function(v){
21060         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21061     },
21062
21063     /**
21064      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21065      * @param {Mixed} value The value to set
21066      */
21067     setValue : function(v){
21068         this.value = v;
21069         if(this.rendered){
21070             this.el.dom.value = (v === null || v === undefined ? '' : v);
21071              this.validate();
21072         }
21073     },
21074
21075     adjustSize : function(w, h){
21076         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21077         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21078         return s;
21079     },
21080
21081     adjustWidth : function(tag, w){
21082         tag = tag.toLowerCase();
21083         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21084             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21085                 if(tag == 'input'){
21086                     return w + 2;
21087                 }
21088                 if(tag == 'textarea'){
21089                     return w-2;
21090                 }
21091             }else if(Roo.isOpera){
21092                 if(tag == 'input'){
21093                     return w + 2;
21094                 }
21095                 if(tag == 'textarea'){
21096                     return w-2;
21097                 }
21098             }
21099         }
21100         return w;
21101     }
21102 });
21103
21104
21105 // anything other than normal should be considered experimental
21106 Roo.form.Field.msgFx = {
21107     normal : {
21108         show: function(msgEl, f){
21109             msgEl.setDisplayed('block');
21110         },
21111
21112         hide : function(msgEl, f){
21113             msgEl.setDisplayed(false).update('');
21114         }
21115     },
21116
21117     slide : {
21118         show: function(msgEl, f){
21119             msgEl.slideIn('t', {stopFx:true});
21120         },
21121
21122         hide : function(msgEl, f){
21123             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21124         }
21125     },
21126
21127     slideRight : {
21128         show: function(msgEl, f){
21129             msgEl.fixDisplay();
21130             msgEl.alignTo(f.el, 'tl-tr');
21131             msgEl.slideIn('l', {stopFx:true});
21132         },
21133
21134         hide : function(msgEl, f){
21135             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21136         }
21137     }
21138 };/*
21139  * Based on:
21140  * Ext JS Library 1.1.1
21141  * Copyright(c) 2006-2007, Ext JS, LLC.
21142  *
21143  * Originally Released Under LGPL - original licence link has changed is not relivant.
21144  *
21145  * Fork - LGPL
21146  * <script type="text/javascript">
21147  */
21148  
21149
21150 /**
21151  * @class Roo.form.TextField
21152  * @extends Roo.form.Field
21153  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21154  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21155  * @constructor
21156  * Creates a new TextField
21157  * @param {Object} config Configuration options
21158  */
21159 Roo.form.TextField = function(config){
21160     Roo.form.TextField.superclass.constructor.call(this, config);
21161     this.addEvents({
21162         /**
21163          * @event autosize
21164          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21165          * according to the default logic, but this event provides a hook for the developer to apply additional
21166          * logic at runtime to resize the field if needed.
21167              * @param {Roo.form.Field} this This text field
21168              * @param {Number} width The new field width
21169              */
21170         autosize : true
21171     });
21172 };
21173
21174 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21175     /**
21176      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21177      */
21178     grow : false,
21179     /**
21180      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21181      */
21182     growMin : 30,
21183     /**
21184      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21185      */
21186     growMax : 800,
21187     /**
21188      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21189      */
21190     vtype : null,
21191     /**
21192      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21193      */
21194     maskRe : null,
21195     /**
21196      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21197      */
21198     disableKeyFilter : false,
21199     /**
21200      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21201      */
21202     allowBlank : true,
21203     /**
21204      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21205      */
21206     minLength : 0,
21207     /**
21208      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21209      */
21210     maxLength : Number.MAX_VALUE,
21211     /**
21212      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21213      */
21214     minLengthText : "The minimum length for this field is {0}",
21215     /**
21216      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21217      */
21218     maxLengthText : "The maximum length for this field is {0}",
21219     /**
21220      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21221      */
21222     selectOnFocus : false,
21223     /**
21224      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21225      */
21226     blankText : "This field is required",
21227     /**
21228      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21229      * If available, this function will be called only after the basic validators all return true, and will be passed the
21230      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21231      */
21232     validator : null,
21233     /**
21234      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21235      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21236      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21237      */
21238     regex : null,
21239     /**
21240      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21241      */
21242     regexText : "",
21243     /**
21244      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21245      */
21246     emptyText : null,
21247    
21248
21249     // private
21250     initEvents : function()
21251     {
21252         if (this.emptyText) {
21253             this.el.attr('placeholder', this.emptyText);
21254         }
21255         
21256         Roo.form.TextField.superclass.initEvents.call(this);
21257         if(this.validationEvent == 'keyup'){
21258             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21259             this.el.on('keyup', this.filterValidation, this);
21260         }
21261         else if(this.validationEvent !== false){
21262             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21263         }
21264         
21265         if(this.selectOnFocus){
21266             this.on("focus", this.preFocus, this);
21267             
21268         }
21269         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21270             this.el.on("keypress", this.filterKeys, this);
21271         }
21272         if(this.grow){
21273             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21274             this.el.on("click", this.autoSize,  this);
21275         }
21276         if(this.el.is('input[type=password]') && Roo.isSafari){
21277             this.el.on('keydown', this.SafariOnKeyDown, this);
21278         }
21279     },
21280
21281     processValue : function(value){
21282         if(this.stripCharsRe){
21283             var newValue = value.replace(this.stripCharsRe, '');
21284             if(newValue !== value){
21285                 this.setRawValue(newValue);
21286                 return newValue;
21287             }
21288         }
21289         return value;
21290     },
21291
21292     filterValidation : function(e){
21293         if(!e.isNavKeyPress()){
21294             this.validationTask.delay(this.validationDelay);
21295         }
21296     },
21297
21298     // private
21299     onKeyUp : function(e){
21300         if(!e.isNavKeyPress()){
21301             this.autoSize();
21302         }
21303     },
21304
21305     /**
21306      * Resets the current field value to the originally-loaded value and clears any validation messages.
21307      *  
21308      */
21309     reset : function(){
21310         Roo.form.TextField.superclass.reset.call(this);
21311        
21312     },
21313
21314     
21315     // private
21316     preFocus : function(){
21317         
21318         if(this.selectOnFocus){
21319             this.el.dom.select();
21320         }
21321     },
21322
21323     
21324     // private
21325     filterKeys : function(e){
21326         var k = e.getKey();
21327         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21328             return;
21329         }
21330         var c = e.getCharCode(), cc = String.fromCharCode(c);
21331         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21332             return;
21333         }
21334         if(!this.maskRe.test(cc)){
21335             e.stopEvent();
21336         }
21337     },
21338
21339     setValue : function(v){
21340         
21341         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21342         
21343         this.autoSize();
21344     },
21345
21346     /**
21347      * Validates a value according to the field's validation rules and marks the field as invalid
21348      * if the validation fails
21349      * @param {Mixed} value The value to validate
21350      * @return {Boolean} True if the value is valid, else false
21351      */
21352     validateValue : function(value){
21353         if(value.length < 1)  { // if it's blank
21354              if(this.allowBlank){
21355                 this.clearInvalid();
21356                 return true;
21357              }else{
21358                 this.markInvalid(this.blankText);
21359                 return false;
21360              }
21361         }
21362         if(value.length < this.minLength){
21363             this.markInvalid(String.format(this.minLengthText, this.minLength));
21364             return false;
21365         }
21366         if(value.length > this.maxLength){
21367             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21368             return false;
21369         }
21370         if(this.vtype){
21371             var vt = Roo.form.VTypes;
21372             if(!vt[this.vtype](value, this)){
21373                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21374                 return false;
21375             }
21376         }
21377         if(typeof this.validator == "function"){
21378             var msg = this.validator(value);
21379             if(msg !== true){
21380                 this.markInvalid(msg);
21381                 return false;
21382             }
21383         }
21384         if(this.regex && !this.regex.test(value)){
21385             this.markInvalid(this.regexText);
21386             return false;
21387         }
21388         return true;
21389     },
21390
21391     /**
21392      * Selects text in this field
21393      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21394      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21395      */
21396     selectText : function(start, end){
21397         var v = this.getRawValue();
21398         if(v.length > 0){
21399             start = start === undefined ? 0 : start;
21400             end = end === undefined ? v.length : end;
21401             var d = this.el.dom;
21402             if(d.setSelectionRange){
21403                 d.setSelectionRange(start, end);
21404             }else if(d.createTextRange){
21405                 var range = d.createTextRange();
21406                 range.moveStart("character", start);
21407                 range.moveEnd("character", v.length-end);
21408                 range.select();
21409             }
21410         }
21411     },
21412
21413     /**
21414      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21415      * This only takes effect if grow = true, and fires the autosize event.
21416      */
21417     autoSize : function(){
21418         if(!this.grow || !this.rendered){
21419             return;
21420         }
21421         if(!this.metrics){
21422             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21423         }
21424         var el = this.el;
21425         var v = el.dom.value;
21426         var d = document.createElement('div');
21427         d.appendChild(document.createTextNode(v));
21428         v = d.innerHTML;
21429         d = null;
21430         v += "&#160;";
21431         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21432         this.el.setWidth(w);
21433         this.fireEvent("autosize", this, w);
21434     },
21435     
21436     // private
21437     SafariOnKeyDown : function(event)
21438     {
21439         // this is a workaround for a password hang bug on chrome/ webkit.
21440         
21441         var isSelectAll = false;
21442         
21443         if(this.el.dom.selectionEnd > 0){
21444             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21445         }
21446         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21447             event.preventDefault();
21448             this.setValue('');
21449             return;
21450         }
21451         
21452         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21453             
21454             event.preventDefault();
21455             // this is very hacky as keydown always get's upper case.
21456             
21457             var cc = String.fromCharCode(event.getCharCode());
21458             
21459             
21460             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21461             
21462         }
21463         
21464         
21465     }
21466 });/*
21467  * Based on:
21468  * Ext JS Library 1.1.1
21469  * Copyright(c) 2006-2007, Ext JS, LLC.
21470  *
21471  * Originally Released Under LGPL - original licence link has changed is not relivant.
21472  *
21473  * Fork - LGPL
21474  * <script type="text/javascript">
21475  */
21476  
21477 /**
21478  * @class Roo.form.Hidden
21479  * @extends Roo.form.TextField
21480  * Simple Hidden element used on forms 
21481  * 
21482  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21483  * 
21484  * @constructor
21485  * Creates a new Hidden form element.
21486  * @param {Object} config Configuration options
21487  */
21488
21489
21490
21491 // easy hidden field...
21492 Roo.form.Hidden = function(config){
21493     Roo.form.Hidden.superclass.constructor.call(this, config);
21494 };
21495   
21496 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21497     fieldLabel:      '',
21498     inputType:      'hidden',
21499     width:          50,
21500     allowBlank:     true,
21501     labelSeparator: '',
21502     hidden:         true,
21503     itemCls :       'x-form-item-display-none'
21504
21505
21506 });
21507
21508
21509 /*
21510  * Based on:
21511  * Ext JS Library 1.1.1
21512  * Copyright(c) 2006-2007, Ext JS, LLC.
21513  *
21514  * Originally Released Under LGPL - original licence link has changed is not relivant.
21515  *
21516  * Fork - LGPL
21517  * <script type="text/javascript">
21518  */
21519  
21520 /**
21521  * @class Roo.form.TriggerField
21522  * @extends Roo.form.TextField
21523  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21524  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21525  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21526  * for which you can provide a custom implementation.  For example:
21527  * <pre><code>
21528 var trigger = new Roo.form.TriggerField();
21529 trigger.onTriggerClick = myTriggerFn;
21530 trigger.applyTo('my-field');
21531 </code></pre>
21532  *
21533  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21534  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21535  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21536  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21537  * @constructor
21538  * Create a new TriggerField.
21539  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21540  * to the base TextField)
21541  */
21542 Roo.form.TriggerField = function(config){
21543     this.mimicing = false;
21544     Roo.form.TriggerField.superclass.constructor.call(this, config);
21545 };
21546
21547 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21548     /**
21549      * @cfg {String} triggerClass A CSS class to apply to the trigger
21550      */
21551     /**
21552      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21553      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21554      */
21555     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21556     /**
21557      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21558      */
21559     hideTrigger:false,
21560
21561     /** @cfg {Boolean} grow @hide */
21562     /** @cfg {Number} growMin @hide */
21563     /** @cfg {Number} growMax @hide */
21564
21565     /**
21566      * @hide 
21567      * @method
21568      */
21569     autoSize: Roo.emptyFn,
21570     // private
21571     monitorTab : true,
21572     // private
21573     deferHeight : true,
21574
21575     
21576     actionMode : 'wrap',
21577     // private
21578     onResize : function(w, h){
21579         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21580         if(typeof w == 'number'){
21581             var x = w - this.trigger.getWidth();
21582             this.el.setWidth(this.adjustWidth('input', x));
21583             this.trigger.setStyle('left', x+'px');
21584         }
21585     },
21586
21587     // private
21588     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21589
21590     // private
21591     getResizeEl : function(){
21592         return this.wrap;
21593     },
21594
21595     // private
21596     getPositionEl : function(){
21597         return this.wrap;
21598     },
21599
21600     // private
21601     alignErrorIcon : function(){
21602         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21603     },
21604
21605     // private
21606     onRender : function(ct, position){
21607         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21608         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21609         this.trigger = this.wrap.createChild(this.triggerConfig ||
21610                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21611         if(this.hideTrigger){
21612             this.trigger.setDisplayed(false);
21613         }
21614         this.initTrigger();
21615         if(!this.width){
21616             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21617         }
21618     },
21619
21620     // private
21621     initTrigger : function(){
21622         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21623         this.trigger.addClassOnOver('x-form-trigger-over');
21624         this.trigger.addClassOnClick('x-form-trigger-click');
21625     },
21626
21627     // private
21628     onDestroy : function(){
21629         if(this.trigger){
21630             this.trigger.removeAllListeners();
21631             this.trigger.remove();
21632         }
21633         if(this.wrap){
21634             this.wrap.remove();
21635         }
21636         Roo.form.TriggerField.superclass.onDestroy.call(this);
21637     },
21638
21639     // private
21640     onFocus : function(){
21641         Roo.form.TriggerField.superclass.onFocus.call(this);
21642         if(!this.mimicing){
21643             this.wrap.addClass('x-trigger-wrap-focus');
21644             this.mimicing = true;
21645             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21646             if(this.monitorTab){
21647                 this.el.on("keydown", this.checkTab, this);
21648             }
21649         }
21650     },
21651
21652     // private
21653     checkTab : function(e){
21654         if(e.getKey() == e.TAB){
21655             this.triggerBlur();
21656         }
21657     },
21658
21659     // private
21660     onBlur : function(){
21661         // do nothing
21662     },
21663
21664     // private
21665     mimicBlur : function(e, t){
21666         if(!this.wrap.contains(t) && this.validateBlur()){
21667             this.triggerBlur();
21668         }
21669     },
21670
21671     // private
21672     triggerBlur : function(){
21673         this.mimicing = false;
21674         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21675         if(this.monitorTab){
21676             this.el.un("keydown", this.checkTab, this);
21677         }
21678         this.wrap.removeClass('x-trigger-wrap-focus');
21679         Roo.form.TriggerField.superclass.onBlur.call(this);
21680     },
21681
21682     // private
21683     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21684     validateBlur : function(e, t){
21685         return true;
21686     },
21687
21688     // private
21689     onDisable : function(){
21690         Roo.form.TriggerField.superclass.onDisable.call(this);
21691         if(this.wrap){
21692             this.wrap.addClass('x-item-disabled');
21693         }
21694     },
21695
21696     // private
21697     onEnable : function(){
21698         Roo.form.TriggerField.superclass.onEnable.call(this);
21699         if(this.wrap){
21700             this.wrap.removeClass('x-item-disabled');
21701         }
21702     },
21703
21704     // private
21705     onShow : function(){
21706         var ae = this.getActionEl();
21707         
21708         if(ae){
21709             ae.dom.style.display = '';
21710             ae.dom.style.visibility = 'visible';
21711         }
21712     },
21713
21714     // private
21715     
21716     onHide : function(){
21717         var ae = this.getActionEl();
21718         ae.dom.style.display = 'none';
21719     },
21720
21721     /**
21722      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21723      * by an implementing function.
21724      * @method
21725      * @param {EventObject} e
21726      */
21727     onTriggerClick : Roo.emptyFn
21728 });
21729
21730 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21731 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21732 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21733 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21734     initComponent : function(){
21735         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21736
21737         this.triggerConfig = {
21738             tag:'span', cls:'x-form-twin-triggers', cn:[
21739             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21740             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21741         ]};
21742     },
21743
21744     getTrigger : function(index){
21745         return this.triggers[index];
21746     },
21747
21748     initTrigger : function(){
21749         var ts = this.trigger.select('.x-form-trigger', true);
21750         this.wrap.setStyle('overflow', 'hidden');
21751         var triggerField = this;
21752         ts.each(function(t, all, index){
21753             t.hide = function(){
21754                 var w = triggerField.wrap.getWidth();
21755                 this.dom.style.display = 'none';
21756                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21757             };
21758             t.show = function(){
21759                 var w = triggerField.wrap.getWidth();
21760                 this.dom.style.display = '';
21761                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21762             };
21763             var triggerIndex = 'Trigger'+(index+1);
21764
21765             if(this['hide'+triggerIndex]){
21766                 t.dom.style.display = 'none';
21767             }
21768             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21769             t.addClassOnOver('x-form-trigger-over');
21770             t.addClassOnClick('x-form-trigger-click');
21771         }, this);
21772         this.triggers = ts.elements;
21773     },
21774
21775     onTrigger1Click : Roo.emptyFn,
21776     onTrigger2Click : Roo.emptyFn
21777 });/*
21778  * Based on:
21779  * Ext JS Library 1.1.1
21780  * Copyright(c) 2006-2007, Ext JS, LLC.
21781  *
21782  * Originally Released Under LGPL - original licence link has changed is not relivant.
21783  *
21784  * Fork - LGPL
21785  * <script type="text/javascript">
21786  */
21787  
21788 /**
21789  * @class Roo.form.TextArea
21790  * @extends Roo.form.TextField
21791  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21792  * support for auto-sizing.
21793  * @constructor
21794  * Creates a new TextArea
21795  * @param {Object} config Configuration options
21796  */
21797 Roo.form.TextArea = function(config){
21798     Roo.form.TextArea.superclass.constructor.call(this, config);
21799     // these are provided exchanges for backwards compat
21800     // minHeight/maxHeight were replaced by growMin/growMax to be
21801     // compatible with TextField growing config values
21802     if(this.minHeight !== undefined){
21803         this.growMin = this.minHeight;
21804     }
21805     if(this.maxHeight !== undefined){
21806         this.growMax = this.maxHeight;
21807     }
21808 };
21809
21810 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21811     /**
21812      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21813      */
21814     growMin : 60,
21815     /**
21816      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21817      */
21818     growMax: 1000,
21819     /**
21820      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21821      * in the field (equivalent to setting overflow: hidden, defaults to false)
21822      */
21823     preventScrollbars: false,
21824     /**
21825      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21826      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21827      */
21828
21829     // private
21830     onRender : function(ct, position){
21831         if(!this.el){
21832             this.defaultAutoCreate = {
21833                 tag: "textarea",
21834                 style:"width:300px;height:60px;",
21835                 autocomplete: "new-password"
21836             };
21837         }
21838         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21839         if(this.grow){
21840             this.textSizeEl = Roo.DomHelper.append(document.body, {
21841                 tag: "pre", cls: "x-form-grow-sizer"
21842             });
21843             if(this.preventScrollbars){
21844                 this.el.setStyle("overflow", "hidden");
21845             }
21846             this.el.setHeight(this.growMin);
21847         }
21848     },
21849
21850     onDestroy : function(){
21851         if(this.textSizeEl){
21852             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21853         }
21854         Roo.form.TextArea.superclass.onDestroy.call(this);
21855     },
21856
21857     // private
21858     onKeyUp : function(e){
21859         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21860             this.autoSize();
21861         }
21862     },
21863
21864     /**
21865      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21866      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21867      */
21868     autoSize : function(){
21869         if(!this.grow || !this.textSizeEl){
21870             return;
21871         }
21872         var el = this.el;
21873         var v = el.dom.value;
21874         var ts = this.textSizeEl;
21875
21876         ts.innerHTML = '';
21877         ts.appendChild(document.createTextNode(v));
21878         v = ts.innerHTML;
21879
21880         Roo.fly(ts).setWidth(this.el.getWidth());
21881         if(v.length < 1){
21882             v = "&#160;&#160;";
21883         }else{
21884             if(Roo.isIE){
21885                 v = v.replace(/\n/g, '<p>&#160;</p>');
21886             }
21887             v += "&#160;\n&#160;";
21888         }
21889         ts.innerHTML = v;
21890         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21891         if(h != this.lastHeight){
21892             this.lastHeight = h;
21893             this.el.setHeight(h);
21894             this.fireEvent("autosize", this, h);
21895         }
21896     }
21897 });/*
21898  * Based on:
21899  * Ext JS Library 1.1.1
21900  * Copyright(c) 2006-2007, Ext JS, LLC.
21901  *
21902  * Originally Released Under LGPL - original licence link has changed is not relivant.
21903  *
21904  * Fork - LGPL
21905  * <script type="text/javascript">
21906  */
21907  
21908
21909 /**
21910  * @class Roo.form.NumberField
21911  * @extends Roo.form.TextField
21912  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21913  * @constructor
21914  * Creates a new NumberField
21915  * @param {Object} config Configuration options
21916  */
21917 Roo.form.NumberField = function(config){
21918     Roo.form.NumberField.superclass.constructor.call(this, config);
21919 };
21920
21921 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21922     /**
21923      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21924      */
21925     fieldClass: "x-form-field x-form-num-field",
21926     /**
21927      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21928      */
21929     allowDecimals : true,
21930     /**
21931      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21932      */
21933     decimalSeparator : ".",
21934     /**
21935      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21936      */
21937     decimalPrecision : 2,
21938     /**
21939      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21940      */
21941     allowNegative : true,
21942     /**
21943      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21944      */
21945     minValue : Number.NEGATIVE_INFINITY,
21946     /**
21947      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21948      */
21949     maxValue : Number.MAX_VALUE,
21950     /**
21951      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21952      */
21953     minText : "The minimum value for this field is {0}",
21954     /**
21955      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21956      */
21957     maxText : "The maximum value for this field is {0}",
21958     /**
21959      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21960      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21961      */
21962     nanText : "{0} is not a valid number",
21963
21964     // private
21965     initEvents : function(){
21966         Roo.form.NumberField.superclass.initEvents.call(this);
21967         var allowed = "0123456789";
21968         if(this.allowDecimals){
21969             allowed += this.decimalSeparator;
21970         }
21971         if(this.allowNegative){
21972             allowed += "-";
21973         }
21974         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21975         var keyPress = function(e){
21976             var k = e.getKey();
21977             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21978                 return;
21979             }
21980             var c = e.getCharCode();
21981             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21982                 e.stopEvent();
21983             }
21984         };
21985         this.el.on("keypress", keyPress, this);
21986     },
21987
21988     // private
21989     validateValue : function(value){
21990         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21991             return false;
21992         }
21993         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21994              return true;
21995         }
21996         var num = this.parseValue(value);
21997         if(isNaN(num)){
21998             this.markInvalid(String.format(this.nanText, value));
21999             return false;
22000         }
22001         if(num < this.minValue){
22002             this.markInvalid(String.format(this.minText, this.minValue));
22003             return false;
22004         }
22005         if(num > this.maxValue){
22006             this.markInvalid(String.format(this.maxText, this.maxValue));
22007             return false;
22008         }
22009         return true;
22010     },
22011
22012     getValue : function(){
22013         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22014     },
22015
22016     // private
22017     parseValue : function(value){
22018         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22019         return isNaN(value) ? '' : value;
22020     },
22021
22022     // private
22023     fixPrecision : function(value){
22024         var nan = isNaN(value);
22025         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22026             return nan ? '' : value;
22027         }
22028         return parseFloat(value).toFixed(this.decimalPrecision);
22029     },
22030
22031     setValue : function(v){
22032         v = this.fixPrecision(v);
22033         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22034     },
22035
22036     // private
22037     decimalPrecisionFcn : function(v){
22038         return Math.floor(v);
22039     },
22040
22041     beforeBlur : function(){
22042         var v = this.parseValue(this.getRawValue());
22043         if(v){
22044             this.setValue(v);
22045         }
22046     }
22047 });/*
22048  * Based on:
22049  * Ext JS Library 1.1.1
22050  * Copyright(c) 2006-2007, Ext JS, LLC.
22051  *
22052  * Originally Released Under LGPL - original licence link has changed is not relivant.
22053  *
22054  * Fork - LGPL
22055  * <script type="text/javascript">
22056  */
22057  
22058 /**
22059  * @class Roo.form.DateField
22060  * @extends Roo.form.TriggerField
22061  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22062 * @constructor
22063 * Create a new DateField
22064 * @param {Object} config
22065  */
22066 Roo.form.DateField = function(config){
22067     Roo.form.DateField.superclass.constructor.call(this, config);
22068     
22069       this.addEvents({
22070          
22071         /**
22072          * @event select
22073          * Fires when a date is selected
22074              * @param {Roo.form.DateField} combo This combo box
22075              * @param {Date} date The date selected
22076              */
22077         'select' : true
22078          
22079     });
22080     
22081     
22082     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22083     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22084     this.ddMatch = null;
22085     if(this.disabledDates){
22086         var dd = this.disabledDates;
22087         var re = "(?:";
22088         for(var i = 0; i < dd.length; i++){
22089             re += dd[i];
22090             if(i != dd.length-1) re += "|";
22091         }
22092         this.ddMatch = new RegExp(re + ")");
22093     }
22094 };
22095
22096 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22097     /**
22098      * @cfg {String} format
22099      * The default date format string which can be overriden for localization support.  The format must be
22100      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22101      */
22102     format : "m/d/y",
22103     /**
22104      * @cfg {String} altFormats
22105      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22106      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22107      */
22108     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22109     /**
22110      * @cfg {Array} disabledDays
22111      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22112      */
22113     disabledDays : null,
22114     /**
22115      * @cfg {String} disabledDaysText
22116      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22117      */
22118     disabledDaysText : "Disabled",
22119     /**
22120      * @cfg {Array} disabledDates
22121      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22122      * expression so they are very powerful. Some examples:
22123      * <ul>
22124      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22125      * <li>["03/08", "09/16"] would disable those days for every year</li>
22126      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22127      * <li>["03/../2006"] would disable every day in March 2006</li>
22128      * <li>["^03"] would disable every day in every March</li>
22129      * </ul>
22130      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22131      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22132      */
22133     disabledDates : null,
22134     /**
22135      * @cfg {String} disabledDatesText
22136      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22137      */
22138     disabledDatesText : "Disabled",
22139     /**
22140      * @cfg {Date/String} minValue
22141      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22142      * valid format (defaults to null).
22143      */
22144     minValue : null,
22145     /**
22146      * @cfg {Date/String} maxValue
22147      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22148      * valid format (defaults to null).
22149      */
22150     maxValue : null,
22151     /**
22152      * @cfg {String} minText
22153      * The error text to display when the date in the cell is before minValue (defaults to
22154      * 'The date in this field must be after {minValue}').
22155      */
22156     minText : "The date in this field must be equal to or after {0}",
22157     /**
22158      * @cfg {String} maxText
22159      * The error text to display when the date in the cell is after maxValue (defaults to
22160      * 'The date in this field must be before {maxValue}').
22161      */
22162     maxText : "The date in this field must be equal to or before {0}",
22163     /**
22164      * @cfg {String} invalidText
22165      * The error text to display when the date in the field is invalid (defaults to
22166      * '{value} is not a valid date - it must be in the format {format}').
22167      */
22168     invalidText : "{0} is not a valid date - it must be in the format {1}",
22169     /**
22170      * @cfg {String} triggerClass
22171      * An additional CSS class used to style the trigger button.  The trigger will always get the
22172      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22173      * which displays a calendar icon).
22174      */
22175     triggerClass : 'x-form-date-trigger',
22176     
22177
22178     /**
22179      * @cfg {Boolean} useIso
22180      * if enabled, then the date field will use a hidden field to store the 
22181      * real value as iso formated date. default (false)
22182      */ 
22183     useIso : false,
22184     /**
22185      * @cfg {String/Object} autoCreate
22186      * A DomHelper element spec, or true for a default element spec (defaults to
22187      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22188      */ 
22189     // private
22190     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22191     
22192     // private
22193     hiddenField: false,
22194     
22195     onRender : function(ct, position)
22196     {
22197         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22198         if (this.useIso) {
22199             //this.el.dom.removeAttribute('name'); 
22200             Roo.log("Changing name?");
22201             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22202             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22203                     'before', true);
22204             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22205             // prevent input submission
22206             this.hiddenName = this.name;
22207         }
22208             
22209             
22210     },
22211     
22212     // private
22213     validateValue : function(value)
22214     {
22215         value = this.formatDate(value);
22216         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22217             Roo.log('super failed');
22218             return false;
22219         }
22220         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22221              return true;
22222         }
22223         var svalue = value;
22224         value = this.parseDate(value);
22225         if(!value){
22226             Roo.log('parse date failed' + svalue);
22227             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22228             return false;
22229         }
22230         var time = value.getTime();
22231         if(this.minValue && time < this.minValue.getTime()){
22232             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22233             return false;
22234         }
22235         if(this.maxValue && time > this.maxValue.getTime()){
22236             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22237             return false;
22238         }
22239         if(this.disabledDays){
22240             var day = value.getDay();
22241             for(var i = 0; i < this.disabledDays.length; i++) {
22242                 if(day === this.disabledDays[i]){
22243                     this.markInvalid(this.disabledDaysText);
22244                     return false;
22245                 }
22246             }
22247         }
22248         var fvalue = this.formatDate(value);
22249         if(this.ddMatch && this.ddMatch.test(fvalue)){
22250             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22251             return false;
22252         }
22253         return true;
22254     },
22255
22256     // private
22257     // Provides logic to override the default TriggerField.validateBlur which just returns true
22258     validateBlur : function(){
22259         return !this.menu || !this.menu.isVisible();
22260     },
22261     
22262     getName: function()
22263     {
22264         // returns hidden if it's set..
22265         if (!this.rendered) {return ''};
22266         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22267         
22268     },
22269
22270     /**
22271      * Returns the current date value of the date field.
22272      * @return {Date} The date value
22273      */
22274     getValue : function(){
22275         
22276         return  this.hiddenField ?
22277                 this.hiddenField.value :
22278                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22279     },
22280
22281     /**
22282      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22283      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22284      * (the default format used is "m/d/y").
22285      * <br />Usage:
22286      * <pre><code>
22287 //All of these calls set the same date value (May 4, 2006)
22288
22289 //Pass a date object:
22290 var dt = new Date('5/4/06');
22291 dateField.setValue(dt);
22292
22293 //Pass a date string (default format):
22294 dateField.setValue('5/4/06');
22295
22296 //Pass a date string (custom format):
22297 dateField.format = 'Y-m-d';
22298 dateField.setValue('2006-5-4');
22299 </code></pre>
22300      * @param {String/Date} date The date or valid date string
22301      */
22302     setValue : function(date){
22303         if (this.hiddenField) {
22304             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22305         }
22306         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22307         // make sure the value field is always stored as a date..
22308         this.value = this.parseDate(date);
22309         
22310         
22311     },
22312
22313     // private
22314     parseDate : function(value){
22315         if(!value || value instanceof Date){
22316             return value;
22317         }
22318         var v = Date.parseDate(value, this.format);
22319          if (!v && this.useIso) {
22320             v = Date.parseDate(value, 'Y-m-d');
22321         }
22322         if(!v && this.altFormats){
22323             if(!this.altFormatsArray){
22324                 this.altFormatsArray = this.altFormats.split("|");
22325             }
22326             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22327                 v = Date.parseDate(value, this.altFormatsArray[i]);
22328             }
22329         }
22330         return v;
22331     },
22332
22333     // private
22334     formatDate : function(date, fmt){
22335         return (!date || !(date instanceof Date)) ?
22336                date : date.dateFormat(fmt || this.format);
22337     },
22338
22339     // private
22340     menuListeners : {
22341         select: function(m, d){
22342             
22343             this.setValue(d);
22344             this.fireEvent('select', this, d);
22345         },
22346         show : function(){ // retain focus styling
22347             this.onFocus();
22348         },
22349         hide : function(){
22350             this.focus.defer(10, this);
22351             var ml = this.menuListeners;
22352             this.menu.un("select", ml.select,  this);
22353             this.menu.un("show", ml.show,  this);
22354             this.menu.un("hide", ml.hide,  this);
22355         }
22356     },
22357
22358     // private
22359     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22360     onTriggerClick : function(){
22361         if(this.disabled){
22362             return;
22363         }
22364         if(this.menu == null){
22365             this.menu = new Roo.menu.DateMenu();
22366         }
22367         Roo.apply(this.menu.picker,  {
22368             showClear: this.allowBlank,
22369             minDate : this.minValue,
22370             maxDate : this.maxValue,
22371             disabledDatesRE : this.ddMatch,
22372             disabledDatesText : this.disabledDatesText,
22373             disabledDays : this.disabledDays,
22374             disabledDaysText : this.disabledDaysText,
22375             format : this.useIso ? 'Y-m-d' : this.format,
22376             minText : String.format(this.minText, this.formatDate(this.minValue)),
22377             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22378         });
22379         this.menu.on(Roo.apply({}, this.menuListeners, {
22380             scope:this
22381         }));
22382         this.menu.picker.setValue(this.getValue() || new Date());
22383         this.menu.show(this.el, "tl-bl?");
22384     },
22385
22386     beforeBlur : function(){
22387         var v = this.parseDate(this.getRawValue());
22388         if(v){
22389             this.setValue(v);
22390         }
22391     },
22392
22393     /*@
22394      * overide
22395      * 
22396      */
22397     isDirty : function() {
22398         if(this.disabled) {
22399             return false;
22400         }
22401         
22402         if(typeof(this.startValue) === 'undefined'){
22403             return false;
22404         }
22405         
22406         return String(this.getValue()) !== String(this.startValue);
22407         
22408     }
22409 });/*
22410  * Based on:
22411  * Ext JS Library 1.1.1
22412  * Copyright(c) 2006-2007, Ext JS, LLC.
22413  *
22414  * Originally Released Under LGPL - original licence link has changed is not relivant.
22415  *
22416  * Fork - LGPL
22417  * <script type="text/javascript">
22418  */
22419  
22420 /**
22421  * @class Roo.form.MonthField
22422  * @extends Roo.form.TriggerField
22423  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22424 * @constructor
22425 * Create a new MonthField
22426 * @param {Object} config
22427  */
22428 Roo.form.MonthField = function(config){
22429     
22430     Roo.form.MonthField.superclass.constructor.call(this, config);
22431     
22432       this.addEvents({
22433          
22434         /**
22435          * @event select
22436          * Fires when a date is selected
22437              * @param {Roo.form.MonthFieeld} combo This combo box
22438              * @param {Date} date The date selected
22439              */
22440         'select' : true
22441          
22442     });
22443     
22444     
22445     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22446     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22447     this.ddMatch = null;
22448     if(this.disabledDates){
22449         var dd = this.disabledDates;
22450         var re = "(?:";
22451         for(var i = 0; i < dd.length; i++){
22452             re += dd[i];
22453             if(i != dd.length-1) re += "|";
22454         }
22455         this.ddMatch = new RegExp(re + ")");
22456     }
22457 };
22458
22459 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22460     /**
22461      * @cfg {String} format
22462      * The default date format string which can be overriden for localization support.  The format must be
22463      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22464      */
22465     format : "M Y",
22466     /**
22467      * @cfg {String} altFormats
22468      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22469      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22470      */
22471     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22472     /**
22473      * @cfg {Array} disabledDays
22474      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22475      */
22476     disabledDays : [0,1,2,3,4,5,6],
22477     /**
22478      * @cfg {String} disabledDaysText
22479      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22480      */
22481     disabledDaysText : "Disabled",
22482     /**
22483      * @cfg {Array} disabledDates
22484      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22485      * expression so they are very powerful. Some examples:
22486      * <ul>
22487      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22488      * <li>["03/08", "09/16"] would disable those days for every year</li>
22489      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22490      * <li>["03/../2006"] would disable every day in March 2006</li>
22491      * <li>["^03"] would disable every day in every March</li>
22492      * </ul>
22493      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22494      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22495      */
22496     disabledDates : null,
22497     /**
22498      * @cfg {String} disabledDatesText
22499      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22500      */
22501     disabledDatesText : "Disabled",
22502     /**
22503      * @cfg {Date/String} minValue
22504      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22505      * valid format (defaults to null).
22506      */
22507     minValue : null,
22508     /**
22509      * @cfg {Date/String} maxValue
22510      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22511      * valid format (defaults to null).
22512      */
22513     maxValue : null,
22514     /**
22515      * @cfg {String} minText
22516      * The error text to display when the date in the cell is before minValue (defaults to
22517      * 'The date in this field must be after {minValue}').
22518      */
22519     minText : "The date in this field must be equal to or after {0}",
22520     /**
22521      * @cfg {String} maxTextf
22522      * The error text to display when the date in the cell is after maxValue (defaults to
22523      * 'The date in this field must be before {maxValue}').
22524      */
22525     maxText : "The date in this field must be equal to or before {0}",
22526     /**
22527      * @cfg {String} invalidText
22528      * The error text to display when the date in the field is invalid (defaults to
22529      * '{value} is not a valid date - it must be in the format {format}').
22530      */
22531     invalidText : "{0} is not a valid date - it must be in the format {1}",
22532     /**
22533      * @cfg {String} triggerClass
22534      * An additional CSS class used to style the trigger button.  The trigger will always get the
22535      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22536      * which displays a calendar icon).
22537      */
22538     triggerClass : 'x-form-date-trigger',
22539     
22540
22541     /**
22542      * @cfg {Boolean} useIso
22543      * if enabled, then the date field will use a hidden field to store the 
22544      * real value as iso formated date. default (true)
22545      */ 
22546     useIso : true,
22547     /**
22548      * @cfg {String/Object} autoCreate
22549      * A DomHelper element spec, or true for a default element spec (defaults to
22550      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22551      */ 
22552     // private
22553     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22554     
22555     // private
22556     hiddenField: false,
22557     
22558     hideMonthPicker : false,
22559     
22560     onRender : function(ct, position)
22561     {
22562         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22563         if (this.useIso) {
22564             this.el.dom.removeAttribute('name'); 
22565             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22566                     'before', true);
22567             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22568             // prevent input submission
22569             this.hiddenName = this.name;
22570         }
22571             
22572             
22573     },
22574     
22575     // private
22576     validateValue : function(value)
22577     {
22578         value = this.formatDate(value);
22579         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22580             return false;
22581         }
22582         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22583              return true;
22584         }
22585         var svalue = value;
22586         value = this.parseDate(value);
22587         if(!value){
22588             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22589             return false;
22590         }
22591         var time = value.getTime();
22592         if(this.minValue && time < this.minValue.getTime()){
22593             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22594             return false;
22595         }
22596         if(this.maxValue && time > this.maxValue.getTime()){
22597             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22598             return false;
22599         }
22600         /*if(this.disabledDays){
22601             var day = value.getDay();
22602             for(var i = 0; i < this.disabledDays.length; i++) {
22603                 if(day === this.disabledDays[i]){
22604                     this.markInvalid(this.disabledDaysText);
22605                     return false;
22606                 }
22607             }
22608         }
22609         */
22610         var fvalue = this.formatDate(value);
22611         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22612             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22613             return false;
22614         }
22615         */
22616         return true;
22617     },
22618
22619     // private
22620     // Provides logic to override the default TriggerField.validateBlur which just returns true
22621     validateBlur : function(){
22622         return !this.menu || !this.menu.isVisible();
22623     },
22624
22625     /**
22626      * Returns the current date value of the date field.
22627      * @return {Date} The date value
22628      */
22629     getValue : function(){
22630         
22631         
22632         
22633         return  this.hiddenField ?
22634                 this.hiddenField.value :
22635                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22636     },
22637
22638     /**
22639      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22640      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22641      * (the default format used is "m/d/y").
22642      * <br />Usage:
22643      * <pre><code>
22644 //All of these calls set the same date value (May 4, 2006)
22645
22646 //Pass a date object:
22647 var dt = new Date('5/4/06');
22648 monthField.setValue(dt);
22649
22650 //Pass a date string (default format):
22651 monthField.setValue('5/4/06');
22652
22653 //Pass a date string (custom format):
22654 monthField.format = 'Y-m-d';
22655 monthField.setValue('2006-5-4');
22656 </code></pre>
22657      * @param {String/Date} date The date or valid date string
22658      */
22659     setValue : function(date){
22660         Roo.log('month setValue' + date);
22661         // can only be first of month..
22662         
22663         var val = this.parseDate(date);
22664         
22665         if (this.hiddenField) {
22666             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22667         }
22668         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22669         this.value = this.parseDate(date);
22670     },
22671
22672     // private
22673     parseDate : function(value){
22674         if(!value || value instanceof Date){
22675             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22676             return value;
22677         }
22678         var v = Date.parseDate(value, this.format);
22679         if (!v && this.useIso) {
22680             v = Date.parseDate(value, 'Y-m-d');
22681         }
22682         if (v) {
22683             // 
22684             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22685         }
22686         
22687         
22688         if(!v && this.altFormats){
22689             if(!this.altFormatsArray){
22690                 this.altFormatsArray = this.altFormats.split("|");
22691             }
22692             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22693                 v = Date.parseDate(value, this.altFormatsArray[i]);
22694             }
22695         }
22696         return v;
22697     },
22698
22699     // private
22700     formatDate : function(date, fmt){
22701         return (!date || !(date instanceof Date)) ?
22702                date : date.dateFormat(fmt || this.format);
22703     },
22704
22705     // private
22706     menuListeners : {
22707         select: function(m, d){
22708             this.setValue(d);
22709             this.fireEvent('select', this, d);
22710         },
22711         show : function(){ // retain focus styling
22712             this.onFocus();
22713         },
22714         hide : function(){
22715             this.focus.defer(10, this);
22716             var ml = this.menuListeners;
22717             this.menu.un("select", ml.select,  this);
22718             this.menu.un("show", ml.show,  this);
22719             this.menu.un("hide", ml.hide,  this);
22720         }
22721     },
22722     // private
22723     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22724     onTriggerClick : function(){
22725         if(this.disabled){
22726             return;
22727         }
22728         if(this.menu == null){
22729             this.menu = new Roo.menu.DateMenu();
22730            
22731         }
22732         
22733         Roo.apply(this.menu.picker,  {
22734             
22735             showClear: this.allowBlank,
22736             minDate : this.minValue,
22737             maxDate : this.maxValue,
22738             disabledDatesRE : this.ddMatch,
22739             disabledDatesText : this.disabledDatesText,
22740             
22741             format : this.useIso ? 'Y-m-d' : this.format,
22742             minText : String.format(this.minText, this.formatDate(this.minValue)),
22743             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22744             
22745         });
22746          this.menu.on(Roo.apply({}, this.menuListeners, {
22747             scope:this
22748         }));
22749        
22750         
22751         var m = this.menu;
22752         var p = m.picker;
22753         
22754         // hide month picker get's called when we called by 'before hide';
22755         
22756         var ignorehide = true;
22757         p.hideMonthPicker  = function(disableAnim){
22758             if (ignorehide) {
22759                 return;
22760             }
22761              if(this.monthPicker){
22762                 Roo.log("hideMonthPicker called");
22763                 if(disableAnim === true){
22764                     this.monthPicker.hide();
22765                 }else{
22766                     this.monthPicker.slideOut('t', {duration:.2});
22767                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22768                     p.fireEvent("select", this, this.value);
22769                     m.hide();
22770                 }
22771             }
22772         }
22773         
22774         Roo.log('picker set value');
22775         Roo.log(this.getValue());
22776         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22777         m.show(this.el, 'tl-bl?');
22778         ignorehide  = false;
22779         // this will trigger hideMonthPicker..
22780         
22781         
22782         // hidden the day picker
22783         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22784         
22785         
22786         
22787       
22788         
22789         p.showMonthPicker.defer(100, p);
22790     
22791         
22792        
22793     },
22794
22795     beforeBlur : function(){
22796         var v = this.parseDate(this.getRawValue());
22797         if(v){
22798             this.setValue(v);
22799         }
22800     }
22801
22802     /** @cfg {Boolean} grow @hide */
22803     /** @cfg {Number} growMin @hide */
22804     /** @cfg {Number} growMax @hide */
22805     /**
22806      * @hide
22807      * @method autoSize
22808      */
22809 });/*
22810  * Based on:
22811  * Ext JS Library 1.1.1
22812  * Copyright(c) 2006-2007, Ext JS, LLC.
22813  *
22814  * Originally Released Under LGPL - original licence link has changed is not relivant.
22815  *
22816  * Fork - LGPL
22817  * <script type="text/javascript">
22818  */
22819  
22820
22821 /**
22822  * @class Roo.form.ComboBox
22823  * @extends Roo.form.TriggerField
22824  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22825  * @constructor
22826  * Create a new ComboBox.
22827  * @param {Object} config Configuration options
22828  */
22829 Roo.form.ComboBox = function(config){
22830     Roo.form.ComboBox.superclass.constructor.call(this, config);
22831     this.addEvents({
22832         /**
22833          * @event expand
22834          * Fires when the dropdown list is expanded
22835              * @param {Roo.form.ComboBox} combo This combo box
22836              */
22837         'expand' : true,
22838         /**
22839          * @event collapse
22840          * Fires when the dropdown list is collapsed
22841              * @param {Roo.form.ComboBox} combo This combo box
22842              */
22843         'collapse' : true,
22844         /**
22845          * @event beforeselect
22846          * Fires before a list item is selected. Return false to cancel the selection.
22847              * @param {Roo.form.ComboBox} combo This combo box
22848              * @param {Roo.data.Record} record The data record returned from the underlying store
22849              * @param {Number} index The index of the selected item in the dropdown list
22850              */
22851         'beforeselect' : true,
22852         /**
22853          * @event select
22854          * Fires when a list item is selected
22855              * @param {Roo.form.ComboBox} combo This combo box
22856              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22857              * @param {Number} index The index of the selected item in the dropdown list
22858              */
22859         'select' : true,
22860         /**
22861          * @event beforequery
22862          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22863          * The event object passed has these properties:
22864              * @param {Roo.form.ComboBox} combo This combo box
22865              * @param {String} query The query
22866              * @param {Boolean} forceAll true to force "all" query
22867              * @param {Boolean} cancel true to cancel the query
22868              * @param {Object} e The query event object
22869              */
22870         'beforequery': true,
22871          /**
22872          * @event add
22873          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22874              * @param {Roo.form.ComboBox} combo This combo box
22875              */
22876         'add' : true,
22877         /**
22878          * @event edit
22879          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22880              * @param {Roo.form.ComboBox} combo This combo box
22881              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22882              */
22883         'edit' : true
22884         
22885         
22886     });
22887     if(this.transform){
22888         this.allowDomMove = false;
22889         var s = Roo.getDom(this.transform);
22890         if(!this.hiddenName){
22891             this.hiddenName = s.name;
22892         }
22893         if(!this.store){
22894             this.mode = 'local';
22895             var d = [], opts = s.options;
22896             for(var i = 0, len = opts.length;i < len; i++){
22897                 var o = opts[i];
22898                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22899                 if(o.selected) {
22900                     this.value = value;
22901                 }
22902                 d.push([value, o.text]);
22903             }
22904             this.store = new Roo.data.SimpleStore({
22905                 'id': 0,
22906                 fields: ['value', 'text'],
22907                 data : d
22908             });
22909             this.valueField = 'value';
22910             this.displayField = 'text';
22911         }
22912         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22913         if(!this.lazyRender){
22914             this.target = true;
22915             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22916             s.parentNode.removeChild(s); // remove it
22917             this.render(this.el.parentNode);
22918         }else{
22919             s.parentNode.removeChild(s); // remove it
22920         }
22921
22922     }
22923     if (this.store) {
22924         this.store = Roo.factory(this.store, Roo.data);
22925     }
22926     
22927     this.selectedIndex = -1;
22928     if(this.mode == 'local'){
22929         if(config.queryDelay === undefined){
22930             this.queryDelay = 10;
22931         }
22932         if(config.minChars === undefined){
22933             this.minChars = 0;
22934         }
22935     }
22936 };
22937
22938 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22939     /**
22940      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22941      */
22942     /**
22943      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22944      * rendering into an Roo.Editor, defaults to false)
22945      */
22946     /**
22947      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22948      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22949      */
22950     /**
22951      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22952      */
22953     /**
22954      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22955      * the dropdown list (defaults to undefined, with no header element)
22956      */
22957
22958      /**
22959      * @cfg {String/Roo.Template} tpl The template to use to render the output
22960      */
22961      
22962     // private
22963     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22964     /**
22965      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22966      */
22967     listWidth: undefined,
22968     /**
22969      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22970      * mode = 'remote' or 'text' if mode = 'local')
22971      */
22972     displayField: undefined,
22973     /**
22974      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22975      * mode = 'remote' or 'value' if mode = 'local'). 
22976      * Note: use of a valueField requires the user make a selection
22977      * in order for a value to be mapped.
22978      */
22979     valueField: undefined,
22980     
22981     
22982     /**
22983      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22984      * field's data value (defaults to the underlying DOM element's name)
22985      */
22986     hiddenName: undefined,
22987     /**
22988      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22989      */
22990     listClass: '',
22991     /**
22992      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22993      */
22994     selectedClass: 'x-combo-selected',
22995     /**
22996      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22997      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22998      * which displays a downward arrow icon).
22999      */
23000     triggerClass : 'x-form-arrow-trigger',
23001     /**
23002      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23003      */
23004     shadow:'sides',
23005     /**
23006      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23007      * anchor positions (defaults to 'tl-bl')
23008      */
23009     listAlign: 'tl-bl?',
23010     /**
23011      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23012      */
23013     maxHeight: 300,
23014     /**
23015      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23016      * query specified by the allQuery config option (defaults to 'query')
23017      */
23018     triggerAction: 'query',
23019     /**
23020      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23021      * (defaults to 4, does not apply if editable = false)
23022      */
23023     minChars : 4,
23024     /**
23025      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23026      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23027      */
23028     typeAhead: false,
23029     /**
23030      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23031      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23032      */
23033     queryDelay: 500,
23034     /**
23035      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23036      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23037      */
23038     pageSize: 0,
23039     /**
23040      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23041      * when editable = true (defaults to false)
23042      */
23043     selectOnFocus:false,
23044     /**
23045      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23046      */
23047     queryParam: 'query',
23048     /**
23049      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23050      * when mode = 'remote' (defaults to 'Loading...')
23051      */
23052     loadingText: 'Loading...',
23053     /**
23054      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23055      */
23056     resizable: false,
23057     /**
23058      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23059      */
23060     handleHeight : 8,
23061     /**
23062      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23063      * traditional select (defaults to true)
23064      */
23065     editable: true,
23066     /**
23067      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23068      */
23069     allQuery: '',
23070     /**
23071      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23072      */
23073     mode: 'remote',
23074     /**
23075      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23076      * listWidth has a higher value)
23077      */
23078     minListWidth : 70,
23079     /**
23080      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23081      * allow the user to set arbitrary text into the field (defaults to false)
23082      */
23083     forceSelection:false,
23084     /**
23085      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23086      * if typeAhead = true (defaults to 250)
23087      */
23088     typeAheadDelay : 250,
23089     /**
23090      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23091      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23092      */
23093     valueNotFoundText : undefined,
23094     /**
23095      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23096      */
23097     blockFocus : false,
23098     
23099     /**
23100      * @cfg {Boolean} disableClear Disable showing of clear button.
23101      */
23102     disableClear : false,
23103     /**
23104      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23105      */
23106     alwaysQuery : false,
23107     
23108     //private
23109     addicon : false,
23110     editicon: false,
23111     
23112     // element that contains real text value.. (when hidden is used..)
23113      
23114     // private
23115     onRender : function(ct, position){
23116         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23117         if(this.hiddenName){
23118             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23119                     'before', true);
23120             this.hiddenField.value =
23121                 this.hiddenValue !== undefined ? this.hiddenValue :
23122                 this.value !== undefined ? this.value : '';
23123
23124             // prevent input submission
23125             this.el.dom.removeAttribute('name');
23126              
23127              
23128         }
23129         if(Roo.isGecko){
23130             this.el.dom.setAttribute('autocomplete', 'off');
23131         }
23132
23133         var cls = 'x-combo-list';
23134
23135         this.list = new Roo.Layer({
23136             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23137         });
23138
23139         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23140         this.list.setWidth(lw);
23141         this.list.swallowEvent('mousewheel');
23142         this.assetHeight = 0;
23143
23144         if(this.title){
23145             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23146             this.assetHeight += this.header.getHeight();
23147         }
23148
23149         this.innerList = this.list.createChild({cls:cls+'-inner'});
23150         this.innerList.on('mouseover', this.onViewOver, this);
23151         this.innerList.on('mousemove', this.onViewMove, this);
23152         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23153         
23154         if(this.allowBlank && !this.pageSize && !this.disableClear){
23155             this.footer = this.list.createChild({cls:cls+'-ft'});
23156             this.pageTb = new Roo.Toolbar(this.footer);
23157            
23158         }
23159         if(this.pageSize){
23160             this.footer = this.list.createChild({cls:cls+'-ft'});
23161             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23162                     {pageSize: this.pageSize});
23163             
23164         }
23165         
23166         if (this.pageTb && this.allowBlank && !this.disableClear) {
23167             var _this = this;
23168             this.pageTb.add(new Roo.Toolbar.Fill(), {
23169                 cls: 'x-btn-icon x-btn-clear',
23170                 text: '&#160;',
23171                 handler: function()
23172                 {
23173                     _this.collapse();
23174                     _this.clearValue();
23175                     _this.onSelect(false, -1);
23176                 }
23177             });
23178         }
23179         if (this.footer) {
23180             this.assetHeight += this.footer.getHeight();
23181         }
23182         
23183
23184         if(!this.tpl){
23185             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23186         }
23187
23188         this.view = new Roo.View(this.innerList, this.tpl, {
23189             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23190         });
23191
23192         this.view.on('click', this.onViewClick, this);
23193
23194         this.store.on('beforeload', this.onBeforeLoad, this);
23195         this.store.on('load', this.onLoad, this);
23196         this.store.on('loadexception', this.onLoadException, this);
23197
23198         if(this.resizable){
23199             this.resizer = new Roo.Resizable(this.list,  {
23200                pinned:true, handles:'se'
23201             });
23202             this.resizer.on('resize', function(r, w, h){
23203                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23204                 this.listWidth = w;
23205                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23206                 this.restrictHeight();
23207             }, this);
23208             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23209         }
23210         if(!this.editable){
23211             this.editable = true;
23212             this.setEditable(false);
23213         }  
23214         
23215         
23216         if (typeof(this.events.add.listeners) != 'undefined') {
23217             
23218             this.addicon = this.wrap.createChild(
23219                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23220        
23221             this.addicon.on('click', function(e) {
23222                 this.fireEvent('add', this);
23223             }, this);
23224         }
23225         if (typeof(this.events.edit.listeners) != 'undefined') {
23226             
23227             this.editicon = this.wrap.createChild(
23228                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23229             if (this.addicon) {
23230                 this.editicon.setStyle('margin-left', '40px');
23231             }
23232             this.editicon.on('click', function(e) {
23233                 
23234                 // we fire even  if inothing is selected..
23235                 this.fireEvent('edit', this, this.lastData );
23236                 
23237             }, this);
23238         }
23239         
23240         
23241         
23242     },
23243
23244     // private
23245     initEvents : function(){
23246         Roo.form.ComboBox.superclass.initEvents.call(this);
23247
23248         this.keyNav = new Roo.KeyNav(this.el, {
23249             "up" : function(e){
23250                 this.inKeyMode = true;
23251                 this.selectPrev();
23252             },
23253
23254             "down" : function(e){
23255                 if(!this.isExpanded()){
23256                     this.onTriggerClick();
23257                 }else{
23258                     this.inKeyMode = true;
23259                     this.selectNext();
23260                 }
23261             },
23262
23263             "enter" : function(e){
23264                 this.onViewClick();
23265                 //return true;
23266             },
23267
23268             "esc" : function(e){
23269                 this.collapse();
23270             },
23271
23272             "tab" : function(e){
23273                 this.onViewClick(false);
23274                 this.fireEvent("specialkey", this, e);
23275                 return true;
23276             },
23277
23278             scope : this,
23279
23280             doRelay : function(foo, bar, hname){
23281                 if(hname == 'down' || this.scope.isExpanded()){
23282                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23283                 }
23284                 return true;
23285             },
23286
23287             forceKeyDown: true
23288         });
23289         this.queryDelay = Math.max(this.queryDelay || 10,
23290                 this.mode == 'local' ? 10 : 250);
23291         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23292         if(this.typeAhead){
23293             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23294         }
23295         if(this.editable !== false){
23296             this.el.on("keyup", this.onKeyUp, this);
23297         }
23298         if(this.forceSelection){
23299             this.on('blur', this.doForce, this);
23300         }
23301     },
23302
23303     onDestroy : function(){
23304         if(this.view){
23305             this.view.setStore(null);
23306             this.view.el.removeAllListeners();
23307             this.view.el.remove();
23308             this.view.purgeListeners();
23309         }
23310         if(this.list){
23311             this.list.destroy();
23312         }
23313         if(this.store){
23314             this.store.un('beforeload', this.onBeforeLoad, this);
23315             this.store.un('load', this.onLoad, this);
23316             this.store.un('loadexception', this.onLoadException, this);
23317         }
23318         Roo.form.ComboBox.superclass.onDestroy.call(this);
23319     },
23320
23321     // private
23322     fireKey : function(e){
23323         if(e.isNavKeyPress() && !this.list.isVisible()){
23324             this.fireEvent("specialkey", this, e);
23325         }
23326     },
23327
23328     // private
23329     onResize: function(w, h){
23330         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23331         
23332         if(typeof w != 'number'){
23333             // we do not handle it!?!?
23334             return;
23335         }
23336         var tw = this.trigger.getWidth();
23337         tw += this.addicon ? this.addicon.getWidth() : 0;
23338         tw += this.editicon ? this.editicon.getWidth() : 0;
23339         var x = w - tw;
23340         this.el.setWidth( this.adjustWidth('input', x));
23341             
23342         this.trigger.setStyle('left', x+'px');
23343         
23344         if(this.list && this.listWidth === undefined){
23345             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23346             this.list.setWidth(lw);
23347             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23348         }
23349         
23350     
23351         
23352     },
23353
23354     /**
23355      * Allow or prevent the user from directly editing the field text.  If false is passed,
23356      * the user will only be able to select from the items defined in the dropdown list.  This method
23357      * is the runtime equivalent of setting the 'editable' config option at config time.
23358      * @param {Boolean} value True to allow the user to directly edit the field text
23359      */
23360     setEditable : function(value){
23361         if(value == this.editable){
23362             return;
23363         }
23364         this.editable = value;
23365         if(!value){
23366             this.el.dom.setAttribute('readOnly', true);
23367             this.el.on('mousedown', this.onTriggerClick,  this);
23368             this.el.addClass('x-combo-noedit');
23369         }else{
23370             this.el.dom.setAttribute('readOnly', false);
23371             this.el.un('mousedown', this.onTriggerClick,  this);
23372             this.el.removeClass('x-combo-noedit');
23373         }
23374     },
23375
23376     // private
23377     onBeforeLoad : function(){
23378         if(!this.hasFocus){
23379             return;
23380         }
23381         this.innerList.update(this.loadingText ?
23382                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23383         this.restrictHeight();
23384         this.selectedIndex = -1;
23385     },
23386
23387     // private
23388     onLoad : function(){
23389         if(!this.hasFocus){
23390             return;
23391         }
23392         if(this.store.getCount() > 0){
23393             this.expand();
23394             this.restrictHeight();
23395             if(this.lastQuery == this.allQuery){
23396                 if(this.editable){
23397                     this.el.dom.select();
23398                 }
23399                 if(!this.selectByValue(this.value, true)){
23400                     this.select(0, true);
23401                 }
23402             }else{
23403                 this.selectNext();
23404                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23405                     this.taTask.delay(this.typeAheadDelay);
23406                 }
23407             }
23408         }else{
23409             this.onEmptyResults();
23410         }
23411         //this.el.focus();
23412     },
23413     // private
23414     onLoadException : function()
23415     {
23416         this.collapse();
23417         Roo.log(this.store.reader.jsonData);
23418         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23419             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23420         }
23421         
23422         
23423     },
23424     // private
23425     onTypeAhead : function(){
23426         if(this.store.getCount() > 0){
23427             var r = this.store.getAt(0);
23428             var newValue = r.data[this.displayField];
23429             var len = newValue.length;
23430             var selStart = this.getRawValue().length;
23431             if(selStart != len){
23432                 this.setRawValue(newValue);
23433                 this.selectText(selStart, newValue.length);
23434             }
23435         }
23436     },
23437
23438     // private
23439     onSelect : function(record, index){
23440         if(this.fireEvent('beforeselect', this, record, index) !== false){
23441             this.setFromData(index > -1 ? record.data : false);
23442             this.collapse();
23443             this.fireEvent('select', this, record, index);
23444         }
23445     },
23446
23447     /**
23448      * Returns the currently selected field value or empty string if no value is set.
23449      * @return {String} value The selected value
23450      */
23451     getValue : function(){
23452         if(this.valueField){
23453             return typeof this.value != 'undefined' ? this.value : '';
23454         }
23455         return Roo.form.ComboBox.superclass.getValue.call(this);
23456     },
23457
23458     /**
23459      * Clears any text/value currently set in the field
23460      */
23461     clearValue : function(){
23462         if(this.hiddenField){
23463             this.hiddenField.value = '';
23464         }
23465         this.value = '';
23466         this.setRawValue('');
23467         this.lastSelectionText = '';
23468         
23469     },
23470
23471     /**
23472      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23473      * will be displayed in the field.  If the value does not match the data value of an existing item,
23474      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23475      * Otherwise the field will be blank (although the value will still be set).
23476      * @param {String} value The value to match
23477      */
23478     setValue : function(v){
23479         var text = v;
23480         if(this.valueField){
23481             var r = this.findRecord(this.valueField, v);
23482             if(r){
23483                 text = r.data[this.displayField];
23484             }else if(this.valueNotFoundText !== undefined){
23485                 text = this.valueNotFoundText;
23486             }
23487         }
23488         this.lastSelectionText = text;
23489         if(this.hiddenField){
23490             this.hiddenField.value = v;
23491         }
23492         Roo.form.ComboBox.superclass.setValue.call(this, text);
23493         this.value = v;
23494     },
23495     /**
23496      * @property {Object} the last set data for the element
23497      */
23498     
23499     lastData : false,
23500     /**
23501      * Sets the value of the field based on a object which is related to the record format for the store.
23502      * @param {Object} value the value to set as. or false on reset?
23503      */
23504     setFromData : function(o){
23505         var dv = ''; // display value
23506         var vv = ''; // value value..
23507         this.lastData = o;
23508         if (this.displayField) {
23509             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23510         } else {
23511             // this is an error condition!!!
23512             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23513         }
23514         
23515         if(this.valueField){
23516             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23517         }
23518         if(this.hiddenField){
23519             this.hiddenField.value = vv;
23520             
23521             this.lastSelectionText = dv;
23522             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23523             this.value = vv;
23524             return;
23525         }
23526         // no hidden field.. - we store the value in 'value', but still display
23527         // display field!!!!
23528         this.lastSelectionText = dv;
23529         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23530         this.value = vv;
23531         
23532         
23533     },
23534     // private
23535     reset : function(){
23536         // overridden so that last data is reset..
23537         this.setValue(this.resetValue);
23538         this.clearInvalid();
23539         this.lastData = false;
23540         if (this.view) {
23541             this.view.clearSelections();
23542         }
23543     },
23544     // private
23545     findRecord : function(prop, value){
23546         var record;
23547         if(this.store.getCount() > 0){
23548             this.store.each(function(r){
23549                 if(r.data[prop] == value){
23550                     record = r;
23551                     return false;
23552                 }
23553                 return true;
23554             });
23555         }
23556         return record;
23557     },
23558     
23559     getName: function()
23560     {
23561         // returns hidden if it's set..
23562         if (!this.rendered) {return ''};
23563         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23564         
23565     },
23566     // private
23567     onViewMove : function(e, t){
23568         this.inKeyMode = false;
23569     },
23570
23571     // private
23572     onViewOver : function(e, t){
23573         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23574             return;
23575         }
23576         var item = this.view.findItemFromChild(t);
23577         if(item){
23578             var index = this.view.indexOf(item);
23579             this.select(index, false);
23580         }
23581     },
23582
23583     // private
23584     onViewClick : function(doFocus)
23585     {
23586         var index = this.view.getSelectedIndexes()[0];
23587         var r = this.store.getAt(index);
23588         if(r){
23589             this.onSelect(r, index);
23590         }
23591         if(doFocus !== false && !this.blockFocus){
23592             this.el.focus();
23593         }
23594     },
23595
23596     // private
23597     restrictHeight : function(){
23598         this.innerList.dom.style.height = '';
23599         var inner = this.innerList.dom;
23600         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23601         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23602         this.list.beginUpdate();
23603         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23604         this.list.alignTo(this.el, this.listAlign);
23605         this.list.endUpdate();
23606     },
23607
23608     // private
23609     onEmptyResults : function(){
23610         this.collapse();
23611     },
23612
23613     /**
23614      * Returns true if the dropdown list is expanded, else false.
23615      */
23616     isExpanded : function(){
23617         return this.list.isVisible();
23618     },
23619
23620     /**
23621      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23622      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23623      * @param {String} value The data value of the item to select
23624      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23625      * selected item if it is not currently in view (defaults to true)
23626      * @return {Boolean} True if the value matched an item in the list, else false
23627      */
23628     selectByValue : function(v, scrollIntoView){
23629         if(v !== undefined && v !== null){
23630             var r = this.findRecord(this.valueField || this.displayField, v);
23631             if(r){
23632                 this.select(this.store.indexOf(r), scrollIntoView);
23633                 return true;
23634             }
23635         }
23636         return false;
23637     },
23638
23639     /**
23640      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23641      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23642      * @param {Number} index The zero-based index of the list item to select
23643      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23644      * selected item if it is not currently in view (defaults to true)
23645      */
23646     select : function(index, scrollIntoView){
23647         this.selectedIndex = index;
23648         this.view.select(index);
23649         if(scrollIntoView !== false){
23650             var el = this.view.getNode(index);
23651             if(el){
23652                 this.innerList.scrollChildIntoView(el, false);
23653             }
23654         }
23655     },
23656
23657     // private
23658     selectNext : function(){
23659         var ct = this.store.getCount();
23660         if(ct > 0){
23661             if(this.selectedIndex == -1){
23662                 this.select(0);
23663             }else if(this.selectedIndex < ct-1){
23664                 this.select(this.selectedIndex+1);
23665             }
23666         }
23667     },
23668
23669     // private
23670     selectPrev : function(){
23671         var ct = this.store.getCount();
23672         if(ct > 0){
23673             if(this.selectedIndex == -1){
23674                 this.select(0);
23675             }else if(this.selectedIndex != 0){
23676                 this.select(this.selectedIndex-1);
23677             }
23678         }
23679     },
23680
23681     // private
23682     onKeyUp : function(e){
23683         if(this.editable !== false && !e.isSpecialKey()){
23684             this.lastKey = e.getKey();
23685             this.dqTask.delay(this.queryDelay);
23686         }
23687     },
23688
23689     // private
23690     validateBlur : function(){
23691         return !this.list || !this.list.isVisible();   
23692     },
23693
23694     // private
23695     initQuery : function(){
23696         this.doQuery(this.getRawValue());
23697     },
23698
23699     // private
23700     doForce : function(){
23701         if(this.el.dom.value.length > 0){
23702             this.el.dom.value =
23703                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23704              
23705         }
23706     },
23707
23708     /**
23709      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23710      * query allowing the query action to be canceled if needed.
23711      * @param {String} query The SQL query to execute
23712      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23713      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23714      * saved in the current store (defaults to false)
23715      */
23716     doQuery : function(q, forceAll){
23717         if(q === undefined || q === null){
23718             q = '';
23719         }
23720         var qe = {
23721             query: q,
23722             forceAll: forceAll,
23723             combo: this,
23724             cancel:false
23725         };
23726         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23727             return false;
23728         }
23729         q = qe.query;
23730         forceAll = qe.forceAll;
23731         if(forceAll === true || (q.length >= this.minChars)){
23732             if(this.lastQuery != q || this.alwaysQuery){
23733                 this.lastQuery = q;
23734                 if(this.mode == 'local'){
23735                     this.selectedIndex = -1;
23736                     if(forceAll){
23737                         this.store.clearFilter();
23738                     }else{
23739                         this.store.filter(this.displayField, q);
23740                     }
23741                     this.onLoad();
23742                 }else{
23743                     this.store.baseParams[this.queryParam] = q;
23744                     this.store.load({
23745                         params: this.getParams(q)
23746                     });
23747                     this.expand();
23748                 }
23749             }else{
23750                 this.selectedIndex = -1;
23751                 this.onLoad();   
23752             }
23753         }
23754     },
23755
23756     // private
23757     getParams : function(q){
23758         var p = {};
23759         //p[this.queryParam] = q;
23760         if(this.pageSize){
23761             p.start = 0;
23762             p.limit = this.pageSize;
23763         }
23764         return p;
23765     },
23766
23767     /**
23768      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23769      */
23770     collapse : function(){
23771         if(!this.isExpanded()){
23772             return;
23773         }
23774         this.list.hide();
23775         Roo.get(document).un('mousedown', this.collapseIf, this);
23776         Roo.get(document).un('mousewheel', this.collapseIf, this);
23777         if (!this.editable) {
23778             Roo.get(document).un('keydown', this.listKeyPress, this);
23779         }
23780         this.fireEvent('collapse', this);
23781     },
23782
23783     // private
23784     collapseIf : function(e){
23785         if(!e.within(this.wrap) && !e.within(this.list)){
23786             this.collapse();
23787         }
23788     },
23789
23790     /**
23791      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23792      */
23793     expand : function(){
23794         if(this.isExpanded() || !this.hasFocus){
23795             return;
23796         }
23797         this.list.alignTo(this.el, this.listAlign);
23798         this.list.show();
23799         Roo.get(document).on('mousedown', this.collapseIf, this);
23800         Roo.get(document).on('mousewheel', this.collapseIf, this);
23801         if (!this.editable) {
23802             Roo.get(document).on('keydown', this.listKeyPress, this);
23803         }
23804         
23805         this.fireEvent('expand', this);
23806     },
23807
23808     // private
23809     // Implements the default empty TriggerField.onTriggerClick function
23810     onTriggerClick : function(){
23811         if(this.disabled){
23812             return;
23813         }
23814         if(this.isExpanded()){
23815             this.collapse();
23816             if (!this.blockFocus) {
23817                 this.el.focus();
23818             }
23819             
23820         }else {
23821             this.hasFocus = true;
23822             if(this.triggerAction == 'all') {
23823                 this.doQuery(this.allQuery, true);
23824             } else {
23825                 this.doQuery(this.getRawValue());
23826             }
23827             if (!this.blockFocus) {
23828                 this.el.focus();
23829             }
23830         }
23831     },
23832     listKeyPress : function(e)
23833     {
23834         //Roo.log('listkeypress');
23835         // scroll to first matching element based on key pres..
23836         if (e.isSpecialKey()) {
23837             return false;
23838         }
23839         var k = String.fromCharCode(e.getKey()).toUpperCase();
23840         //Roo.log(k);
23841         var match  = false;
23842         var csel = this.view.getSelectedNodes();
23843         var cselitem = false;
23844         if (csel.length) {
23845             var ix = this.view.indexOf(csel[0]);
23846             cselitem  = this.store.getAt(ix);
23847             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23848                 cselitem = false;
23849             }
23850             
23851         }
23852         
23853         this.store.each(function(v) { 
23854             if (cselitem) {
23855                 // start at existing selection.
23856                 if (cselitem.id == v.id) {
23857                     cselitem = false;
23858                 }
23859                 return;
23860             }
23861                 
23862             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23863                 match = this.store.indexOf(v);
23864                 return false;
23865             }
23866         }, this);
23867         
23868         if (match === false) {
23869             return true; // no more action?
23870         }
23871         // scroll to?
23872         this.view.select(match);
23873         var sn = Roo.get(this.view.getSelectedNodes()[0])
23874         sn.scrollIntoView(sn.dom.parentNode, false);
23875     }
23876
23877     /** 
23878     * @cfg {Boolean} grow 
23879     * @hide 
23880     */
23881     /** 
23882     * @cfg {Number} growMin 
23883     * @hide 
23884     */
23885     /** 
23886     * @cfg {Number} growMax 
23887     * @hide 
23888     */
23889     /**
23890      * @hide
23891      * @method autoSize
23892      */
23893 });/*
23894  * Copyright(c) 2010-2012, Roo J Solutions Limited
23895  *
23896  * Licence LGPL
23897  *
23898  */
23899
23900 /**
23901  * @class Roo.form.ComboBoxArray
23902  * @extends Roo.form.TextField
23903  * A facebook style adder... for lists of email / people / countries  etc...
23904  * pick multiple items from a combo box, and shows each one.
23905  *
23906  *  Fred [x]  Brian [x]  [Pick another |v]
23907  *
23908  *
23909  *  For this to work: it needs various extra information
23910  *    - normal combo problay has
23911  *      name, hiddenName
23912  *    + displayField, valueField
23913  *
23914  *    For our purpose...
23915  *
23916  *
23917  *   If we change from 'extends' to wrapping...
23918  *   
23919  *  
23920  *
23921  
23922  
23923  * @constructor
23924  * Create a new ComboBoxArray.
23925  * @param {Object} config Configuration options
23926  */
23927  
23928
23929 Roo.form.ComboBoxArray = function(config)
23930 {
23931     this.addEvents({
23932         /**
23933          * @event beforeremove
23934          * Fires before remove the value from the list
23935              * @param {Roo.form.ComboBoxArray} _self This combo box array
23936              * @param {Roo.form.ComboBoxArray.Item} item removed item
23937              */
23938         'beforeremove' : true,
23939         /**
23940          * @event remove
23941          * Fires when remove the value from the list
23942              * @param {Roo.form.ComboBoxArray} _self This combo box array
23943              * @param {Roo.form.ComboBoxArray.Item} item removed item
23944              */
23945         'remove' : true
23946         
23947         
23948     });
23949     
23950     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23951     
23952     this.items = new Roo.util.MixedCollection(false);
23953     
23954     // construct the child combo...
23955     
23956     
23957     
23958     
23959    
23960     
23961 }
23962
23963  
23964 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23965
23966     /**
23967      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23968      */
23969     
23970     lastData : false,
23971     
23972     // behavies liek a hiddne field
23973     inputType:      'hidden',
23974     /**
23975      * @cfg {Number} width The width of the box that displays the selected element
23976      */ 
23977     width:          300,
23978
23979     
23980     
23981     /**
23982      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23983      */
23984     name : false,
23985     /**
23986      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23987      */
23988     hiddenName : false,
23989     
23990     
23991     // private the array of items that are displayed..
23992     items  : false,
23993     // private - the hidden field el.
23994     hiddenEl : false,
23995     // private - the filed el..
23996     el : false,
23997     
23998     //validateValue : function() { return true; }, // all values are ok!
23999     //onAddClick: function() { },
24000     
24001     onRender : function(ct, position) 
24002     {
24003         
24004         // create the standard hidden element
24005         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24006         
24007         
24008         // give fake names to child combo;
24009         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24010         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24011         
24012         this.combo = Roo.factory(this.combo, Roo.form);
24013         this.combo.onRender(ct, position);
24014         if (typeof(this.combo.width) != 'undefined') {
24015             this.combo.onResize(this.combo.width,0);
24016         }
24017         
24018         this.combo.initEvents();
24019         
24020         // assigned so form know we need to do this..
24021         this.store          = this.combo.store;
24022         this.valueField     = this.combo.valueField;
24023         this.displayField   = this.combo.displayField ;
24024         
24025         
24026         this.combo.wrap.addClass('x-cbarray-grp');
24027         
24028         var cbwrap = this.combo.wrap.createChild(
24029             {tag: 'div', cls: 'x-cbarray-cb'},
24030             this.combo.el.dom
24031         );
24032         
24033              
24034         this.hiddenEl = this.combo.wrap.createChild({
24035             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24036         });
24037         this.el = this.combo.wrap.createChild({
24038             tag: 'input',  type:'hidden' , name: this.name, value : ''
24039         });
24040          //   this.el.dom.removeAttribute("name");
24041         
24042         
24043         this.outerWrap = this.combo.wrap;
24044         this.wrap = cbwrap;
24045         
24046         this.outerWrap.setWidth(this.width);
24047         this.outerWrap.dom.removeChild(this.el.dom);
24048         
24049         this.wrap.dom.appendChild(this.el.dom);
24050         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24051         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24052         
24053         this.combo.trigger.setStyle('position','relative');
24054         this.combo.trigger.setStyle('left', '0px');
24055         this.combo.trigger.setStyle('top', '2px');
24056         
24057         this.combo.el.setStyle('vertical-align', 'text-bottom');
24058         
24059         //this.trigger.setStyle('vertical-align', 'top');
24060         
24061         // this should use the code from combo really... on('add' ....)
24062         if (this.adder) {
24063             
24064         
24065             this.adder = this.outerWrap.createChild(
24066                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24067             var _t = this;
24068             this.adder.on('click', function(e) {
24069                 _t.fireEvent('adderclick', this, e);
24070             }, _t);
24071         }
24072         //var _t = this;
24073         //this.adder.on('click', this.onAddClick, _t);
24074         
24075         
24076         this.combo.on('select', function(cb, rec, ix) {
24077             this.addItem(rec.data);
24078             
24079             cb.setValue('');
24080             cb.el.dom.value = '';
24081             //cb.lastData = rec.data;
24082             // add to list
24083             
24084         }, this);
24085         
24086         
24087     },
24088     
24089     
24090     getName: function()
24091     {
24092         // returns hidden if it's set..
24093         if (!this.rendered) {return ''};
24094         return  this.hiddenName ? this.hiddenName : this.name;
24095         
24096     },
24097     
24098     
24099     onResize: function(w, h){
24100         
24101         return;
24102         // not sure if this is needed..
24103         //this.combo.onResize(w,h);
24104         
24105         if(typeof w != 'number'){
24106             // we do not handle it!?!?
24107             return;
24108         }
24109         var tw = this.combo.trigger.getWidth();
24110         tw += this.addicon ? this.addicon.getWidth() : 0;
24111         tw += this.editicon ? this.editicon.getWidth() : 0;
24112         var x = w - tw;
24113         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24114             
24115         this.combo.trigger.setStyle('left', '0px');
24116         
24117         if(this.list && this.listWidth === undefined){
24118             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24119             this.list.setWidth(lw);
24120             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24121         }
24122         
24123     
24124         
24125     },
24126     
24127     addItem: function(rec)
24128     {
24129         var valueField = this.combo.valueField;
24130         var displayField = this.combo.displayField;
24131         if (this.items.indexOfKey(rec[valueField]) > -1) {
24132             //console.log("GOT " + rec.data.id);
24133             return;
24134         }
24135         
24136         var x = new Roo.form.ComboBoxArray.Item({
24137             //id : rec[this.idField],
24138             data : rec,
24139             displayField : displayField ,
24140             tipField : displayField ,
24141             cb : this
24142         });
24143         // use the 
24144         this.items.add(rec[valueField],x);
24145         // add it before the element..
24146         this.updateHiddenEl();
24147         x.render(this.outerWrap, this.wrap.dom);
24148         // add the image handler..
24149     },
24150     
24151     updateHiddenEl : function()
24152     {
24153         this.validate();
24154         if (!this.hiddenEl) {
24155             return;
24156         }
24157         var ar = [];
24158         var idField = this.combo.valueField;
24159         
24160         this.items.each(function(f) {
24161             ar.push(f.data[idField]);
24162            
24163         });
24164         this.hiddenEl.dom.value = ar.join(',');
24165         this.validate();
24166     },
24167     
24168     reset : function()
24169     {
24170         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24171         this.items.each(function(f) {
24172            f.remove(); 
24173         });
24174         this.el.dom.value = '';
24175         if (this.hiddenEl) {
24176             this.hiddenEl.dom.value = '';
24177         }
24178         
24179     },
24180     getValue: function()
24181     {
24182         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24183     },
24184     setValue: function(v) // not a valid action - must use addItems..
24185     {
24186          
24187         this.reset();
24188         
24189         
24190         
24191         if (this.store.isLocal && (typeof(v) == 'string')) {
24192             // then we can use the store to find the values..
24193             // comma seperated at present.. this needs to allow JSON based encoding..
24194             this.hiddenEl.value  = v;
24195             var v_ar = [];
24196             Roo.each(v.split(','), function(k) {
24197                 Roo.log("CHECK " + this.valueField + ',' + k);
24198                 var li = this.store.query(this.valueField, k);
24199                 if (!li.length) {
24200                     return;
24201                 }
24202                 var add = {};
24203                 add[this.valueField] = k;
24204                 add[this.displayField] = li.item(0).data[this.displayField];
24205                 
24206                 this.addItem(add);
24207             }, this) 
24208              
24209         }
24210         if (typeof(v) == 'object' ) {
24211             // then let's assume it's an array of objects..
24212             Roo.each(v, function(l) {
24213                 this.addItem(l);
24214             }, this);
24215              
24216         }
24217         
24218         
24219     },
24220     setFromData: function(v)
24221     {
24222         // this recieves an object, if setValues is called.
24223         this.reset();
24224         this.el.dom.value = v[this.displayField];
24225         this.hiddenEl.dom.value = v[this.valueField];
24226         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24227             return;
24228         }
24229         var kv = v[this.valueField];
24230         var dv = v[this.displayField];
24231         kv = typeof(kv) != 'string' ? '' : kv;
24232         dv = typeof(dv) != 'string' ? '' : dv;
24233         
24234         
24235         var keys = kv.split(',');
24236         var display = dv.split(',');
24237         for (var i = 0 ; i < keys.length; i++) {
24238             
24239             add = {};
24240             add[this.valueField] = keys[i];
24241             add[this.displayField] = display[i];
24242             this.addItem(add);
24243         }
24244       
24245         
24246     },
24247     
24248     /**
24249      * Validates the combox array value
24250      * @return {Boolean} True if the value is valid, else false
24251      */
24252     validate : function(){
24253         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24254             this.clearInvalid();
24255             return true;
24256         }
24257         return false;
24258     },
24259     
24260     validateValue : function(value){
24261         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24262         
24263     },
24264     
24265     /*@
24266      * overide
24267      * 
24268      */
24269     isDirty : function() {
24270         if(this.disabled) {
24271             return false;
24272         }
24273         
24274         try {
24275             var d = Roo.decode(String(this.originalValue));
24276         } catch (e) {
24277             return String(this.getValue()) !== String(this.originalValue);
24278         }
24279         
24280         var originalValue = [];
24281         
24282         for (var i = 0; i < d.length; i++){
24283             originalValue.push(d[i][this.valueField]);
24284         }
24285         
24286         return String(this.getValue()) !== String(originalValue.join(','));
24287         
24288     }
24289     
24290 });
24291
24292
24293
24294 /**
24295  * @class Roo.form.ComboBoxArray.Item
24296  * @extends Roo.BoxComponent
24297  * A selected item in the list
24298  *  Fred [x]  Brian [x]  [Pick another |v]
24299  * 
24300  * @constructor
24301  * Create a new item.
24302  * @param {Object} config Configuration options
24303  */
24304  
24305 Roo.form.ComboBoxArray.Item = function(config) {
24306     config.id = Roo.id();
24307     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24308 }
24309
24310 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24311     data : {},
24312     cb: false,
24313     displayField : false,
24314     tipField : false,
24315     
24316     
24317     defaultAutoCreate : {
24318         tag: 'div',
24319         cls: 'x-cbarray-item',
24320         cn : [ 
24321             { tag: 'div' },
24322             {
24323                 tag: 'img',
24324                 width:16,
24325                 height : 16,
24326                 src : Roo.BLANK_IMAGE_URL ,
24327                 align: 'center'
24328             }
24329         ]
24330         
24331     },
24332     
24333  
24334     onRender : function(ct, position)
24335     {
24336         Roo.form.Field.superclass.onRender.call(this, ct, position);
24337         
24338         if(!this.el){
24339             var cfg = this.getAutoCreate();
24340             this.el = ct.createChild(cfg, position);
24341         }
24342         
24343         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24344         
24345         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24346             this.cb.renderer(this.data) :
24347             String.format('{0}',this.data[this.displayField]);
24348         
24349             
24350         this.el.child('div').dom.setAttribute('qtip',
24351                         String.format('{0}',this.data[this.tipField])
24352         );
24353         
24354         this.el.child('img').on('click', this.remove, this);
24355         
24356     },
24357    
24358     remove : function()
24359     {
24360         if(this.cb.disabled){
24361             return;
24362         }
24363         
24364         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24365             this.cb.items.remove(this);
24366             this.el.child('img').un('click', this.remove, this);
24367             this.el.remove();
24368             this.cb.updateHiddenEl();
24369
24370             this.cb.fireEvent('remove', this.cb, this);
24371         }
24372         
24373     }
24374 });/*
24375  * Based on:
24376  * Ext JS Library 1.1.1
24377  * Copyright(c) 2006-2007, Ext JS, LLC.
24378  *
24379  * Originally Released Under LGPL - original licence link has changed is not relivant.
24380  *
24381  * Fork - LGPL
24382  * <script type="text/javascript">
24383  */
24384 /**
24385  * @class Roo.form.Checkbox
24386  * @extends Roo.form.Field
24387  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24388  * @constructor
24389  * Creates a new Checkbox
24390  * @param {Object} config Configuration options
24391  */
24392 Roo.form.Checkbox = function(config){
24393     Roo.form.Checkbox.superclass.constructor.call(this, config);
24394     this.addEvents({
24395         /**
24396          * @event check
24397          * Fires when the checkbox is checked or unchecked.
24398              * @param {Roo.form.Checkbox} this This checkbox
24399              * @param {Boolean} checked The new checked value
24400              */
24401         check : true
24402     });
24403 };
24404
24405 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24406     /**
24407      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24408      */
24409     focusClass : undefined,
24410     /**
24411      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24412      */
24413     fieldClass: "x-form-field",
24414     /**
24415      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24416      */
24417     checked: false,
24418     /**
24419      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24420      * {tag: "input", type: "checkbox", autocomplete: "off"})
24421      */
24422     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24423     /**
24424      * @cfg {String} boxLabel The text that appears beside the checkbox
24425      */
24426     boxLabel : "",
24427     /**
24428      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24429      */  
24430     inputValue : '1',
24431     /**
24432      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24433      */
24434      valueOff: '0', // value when not checked..
24435
24436     actionMode : 'viewEl', 
24437     //
24438     // private
24439     itemCls : 'x-menu-check-item x-form-item',
24440     groupClass : 'x-menu-group-item',
24441     inputType : 'hidden',
24442     
24443     
24444     inSetChecked: false, // check that we are not calling self...
24445     
24446     inputElement: false, // real input element?
24447     basedOn: false, // ????
24448     
24449     isFormField: true, // not sure where this is needed!!!!
24450
24451     onResize : function(){
24452         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24453         if(!this.boxLabel){
24454             this.el.alignTo(this.wrap, 'c-c');
24455         }
24456     },
24457
24458     initEvents : function(){
24459         Roo.form.Checkbox.superclass.initEvents.call(this);
24460         this.el.on("click", this.onClick,  this);
24461         this.el.on("change", this.onClick,  this);
24462     },
24463
24464
24465     getResizeEl : function(){
24466         return this.wrap;
24467     },
24468
24469     getPositionEl : function(){
24470         return this.wrap;
24471     },
24472
24473     // private
24474     onRender : function(ct, position){
24475         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24476         /*
24477         if(this.inputValue !== undefined){
24478             this.el.dom.value = this.inputValue;
24479         }
24480         */
24481         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24482         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24483         var viewEl = this.wrap.createChild({ 
24484             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24485         this.viewEl = viewEl;   
24486         this.wrap.on('click', this.onClick,  this); 
24487         
24488         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24489         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24490         
24491         
24492         
24493         if(this.boxLabel){
24494             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24495         //    viewEl.on('click', this.onClick,  this); 
24496         }
24497         //if(this.checked){
24498             this.setChecked(this.checked);
24499         //}else{
24500             //this.checked = this.el.dom;
24501         //}
24502
24503     },
24504
24505     // private
24506     initValue : Roo.emptyFn,
24507
24508     /**
24509      * Returns the checked state of the checkbox.
24510      * @return {Boolean} True if checked, else false
24511      */
24512     getValue : function(){
24513         if(this.el){
24514             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24515         }
24516         return this.valueOff;
24517         
24518     },
24519
24520         // private
24521     onClick : function(){ 
24522         if (this.disabled) {
24523             return;
24524         }
24525         this.setChecked(!this.checked);
24526
24527         //if(this.el.dom.checked != this.checked){
24528         //    this.setValue(this.el.dom.checked);
24529        // }
24530     },
24531
24532     /**
24533      * Sets the checked state of the checkbox.
24534      * On is always based on a string comparison between inputValue and the param.
24535      * @param {Boolean/String} value - the value to set 
24536      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24537      */
24538     setValue : function(v,suppressEvent){
24539         
24540         
24541         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24542         //if(this.el && this.el.dom){
24543         //    this.el.dom.checked = this.checked;
24544         //    this.el.dom.defaultChecked = this.checked;
24545         //}
24546         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24547         //this.fireEvent("check", this, this.checked);
24548     },
24549     // private..
24550     setChecked : function(state,suppressEvent)
24551     {
24552         if (this.inSetChecked) {
24553             this.checked = state;
24554             return;
24555         }
24556         
24557     
24558         if(this.wrap){
24559             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24560         }
24561         this.checked = state;
24562         if(suppressEvent !== true){
24563             this.fireEvent('check', this, state);
24564         }
24565         this.inSetChecked = true;
24566         this.el.dom.value = state ? this.inputValue : this.valueOff;
24567         this.inSetChecked = false;
24568         
24569     },
24570     // handle setting of hidden value by some other method!!?!?
24571     setFromHidden: function()
24572     {
24573         if(!this.el){
24574             return;
24575         }
24576         //console.log("SET FROM HIDDEN");
24577         //alert('setFrom hidden');
24578         this.setValue(this.el.dom.value);
24579     },
24580     
24581     onDestroy : function()
24582     {
24583         if(this.viewEl){
24584             Roo.get(this.viewEl).remove();
24585         }
24586          
24587         Roo.form.Checkbox.superclass.onDestroy.call(this);
24588     }
24589
24590 });/*
24591  * Based on:
24592  * Ext JS Library 1.1.1
24593  * Copyright(c) 2006-2007, Ext JS, LLC.
24594  *
24595  * Originally Released Under LGPL - original licence link has changed is not relivant.
24596  *
24597  * Fork - LGPL
24598  * <script type="text/javascript">
24599  */
24600  
24601 /**
24602  * @class Roo.form.Radio
24603  * @extends Roo.form.Checkbox
24604  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24605  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24606  * @constructor
24607  * Creates a new Radio
24608  * @param {Object} config Configuration options
24609  */
24610 Roo.form.Radio = function(){
24611     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24612 };
24613 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24614     inputType: 'radio',
24615
24616     /**
24617      * If this radio is part of a group, it will return the selected value
24618      * @return {String}
24619      */
24620     getGroupValue : function(){
24621         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24622     },
24623     
24624     
24625     onRender : function(ct, position){
24626         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24627         
24628         if(this.inputValue !== undefined){
24629             this.el.dom.value = this.inputValue;
24630         }
24631          
24632         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24633         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24634         //var viewEl = this.wrap.createChild({ 
24635         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24636         //this.viewEl = viewEl;   
24637         //this.wrap.on('click', this.onClick,  this); 
24638         
24639         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24640         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24641         
24642         
24643         
24644         if(this.boxLabel){
24645             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24646         //    viewEl.on('click', this.onClick,  this); 
24647         }
24648          if(this.checked){
24649             this.el.dom.checked =   'checked' ;
24650         }
24651          
24652     } 
24653     
24654     
24655 });//<script type="text/javascript">
24656
24657 /*
24658  * Based  Ext JS Library 1.1.1
24659  * Copyright(c) 2006-2007, Ext JS, LLC.
24660  * LGPL
24661  *
24662  */
24663  
24664 /**
24665  * @class Roo.HtmlEditorCore
24666  * @extends Roo.Component
24667  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24668  *
24669  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24670  */
24671
24672 Roo.HtmlEditorCore = function(config){
24673     
24674     
24675     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24676     
24677     
24678     this.addEvents({
24679         /**
24680          * @event initialize
24681          * Fires when the editor is fully initialized (including the iframe)
24682          * @param {Roo.HtmlEditorCore} this
24683          */
24684         initialize: true,
24685         /**
24686          * @event activate
24687          * Fires when the editor is first receives the focus. Any insertion must wait
24688          * until after this event.
24689          * @param {Roo.HtmlEditorCore} this
24690          */
24691         activate: true,
24692          /**
24693          * @event beforesync
24694          * Fires before the textarea is updated with content from the editor iframe. Return false
24695          * to cancel the sync.
24696          * @param {Roo.HtmlEditorCore} this
24697          * @param {String} html
24698          */
24699         beforesync: true,
24700          /**
24701          * @event beforepush
24702          * Fires before the iframe editor is updated with content from the textarea. Return false
24703          * to cancel the push.
24704          * @param {Roo.HtmlEditorCore} this
24705          * @param {String} html
24706          */
24707         beforepush: true,
24708          /**
24709          * @event sync
24710          * Fires when the textarea is updated with content from the editor iframe.
24711          * @param {Roo.HtmlEditorCore} this
24712          * @param {String} html
24713          */
24714         sync: true,
24715          /**
24716          * @event push
24717          * Fires when the iframe editor is updated with content from the textarea.
24718          * @param {Roo.HtmlEditorCore} this
24719          * @param {String} html
24720          */
24721         push: true,
24722         
24723         /**
24724          * @event editorevent
24725          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24726          * @param {Roo.HtmlEditorCore} this
24727          */
24728         editorevent: true
24729         
24730     });
24731     
24732     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24733     
24734     // defaults : white / black...
24735     this.applyBlacklists();
24736     
24737     
24738     
24739 };
24740
24741
24742 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24743
24744
24745      /**
24746      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24747      */
24748     
24749     owner : false,
24750     
24751      /**
24752      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24753      *                        Roo.resizable.
24754      */
24755     resizable : false,
24756      /**
24757      * @cfg {Number} height (in pixels)
24758      */   
24759     height: 300,
24760    /**
24761      * @cfg {Number} width (in pixels)
24762      */   
24763     width: 500,
24764     
24765     /**
24766      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24767      * 
24768      */
24769     stylesheets: false,
24770     
24771     // id of frame..
24772     frameId: false,
24773     
24774     // private properties
24775     validationEvent : false,
24776     deferHeight: true,
24777     initialized : false,
24778     activated : false,
24779     sourceEditMode : false,
24780     onFocus : Roo.emptyFn,
24781     iframePad:3,
24782     hideMode:'offsets',
24783     
24784     clearUp: true,
24785     
24786     // blacklist + whitelisted elements..
24787     black: false,
24788     white: false,
24789      
24790     
24791
24792     /**
24793      * Protected method that will not generally be called directly. It
24794      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24795      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24796      */
24797     getDocMarkup : function(){
24798         // body styles..
24799         var st = '';
24800         
24801         // inherit styels from page...?? 
24802         if (this.stylesheets === false) {
24803             
24804             Roo.get(document.head).select('style').each(function(node) {
24805                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24806             });
24807             
24808             Roo.get(document.head).select('link').each(function(node) { 
24809                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24810             });
24811             
24812         } else if (!this.stylesheets.length) {
24813                 // simple..
24814                 st = '<style type="text/css">' +
24815                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24816                    '</style>';
24817         } else { 
24818             
24819         }
24820         
24821         st +=  '<style type="text/css">' +
24822             'IMG { cursor: pointer } ' +
24823         '</style>';
24824
24825         
24826         return '<html><head>' + st  +
24827             //<style type="text/css">' +
24828             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24829             //'</style>' +
24830             ' </head><body class="roo-htmleditor-body"></body></html>';
24831     },
24832
24833     // private
24834     onRender : function(ct, position)
24835     {
24836         var _t = this;
24837         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24838         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24839         
24840         
24841         this.el.dom.style.border = '0 none';
24842         this.el.dom.setAttribute('tabIndex', -1);
24843         this.el.addClass('x-hidden hide');
24844         
24845         
24846         
24847         if(Roo.isIE){ // fix IE 1px bogus margin
24848             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24849         }
24850        
24851         
24852         this.frameId = Roo.id();
24853         
24854          
24855         
24856         var iframe = this.owner.wrap.createChild({
24857             tag: 'iframe',
24858             cls: 'form-control', // bootstrap..
24859             id: this.frameId,
24860             name: this.frameId,
24861             frameBorder : 'no',
24862             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24863         }, this.el
24864         );
24865         
24866         
24867         this.iframe = iframe.dom;
24868
24869          this.assignDocWin();
24870         
24871         this.doc.designMode = 'on';
24872        
24873         this.doc.open();
24874         this.doc.write(this.getDocMarkup());
24875         this.doc.close();
24876
24877         
24878         var task = { // must defer to wait for browser to be ready
24879             run : function(){
24880                 //console.log("run task?" + this.doc.readyState);
24881                 this.assignDocWin();
24882                 if(this.doc.body || this.doc.readyState == 'complete'){
24883                     try {
24884                         this.doc.designMode="on";
24885                     } catch (e) {
24886                         return;
24887                     }
24888                     Roo.TaskMgr.stop(task);
24889                     this.initEditor.defer(10, this);
24890                 }
24891             },
24892             interval : 10,
24893             duration: 10000,
24894             scope: this
24895         };
24896         Roo.TaskMgr.start(task);
24897
24898     },
24899
24900     // private
24901     onResize : function(w, h)
24902     {
24903          Roo.log('resize: ' +w + ',' + h );
24904         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24905         if(!this.iframe){
24906             return;
24907         }
24908         if(typeof w == 'number'){
24909             
24910             this.iframe.style.width = w + 'px';
24911         }
24912         if(typeof h == 'number'){
24913             
24914             this.iframe.style.height = h + 'px';
24915             if(this.doc){
24916                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24917             }
24918         }
24919         
24920     },
24921
24922     /**
24923      * Toggles the editor between standard and source edit mode.
24924      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24925      */
24926     toggleSourceEdit : function(sourceEditMode){
24927         
24928         this.sourceEditMode = sourceEditMode === true;
24929         
24930         if(this.sourceEditMode){
24931  
24932             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24933             
24934         }else{
24935             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24936             //this.iframe.className = '';
24937             this.deferFocus();
24938         }
24939         //this.setSize(this.owner.wrap.getSize());
24940         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24941     },
24942
24943     
24944   
24945
24946     /**
24947      * Protected method that will not generally be called directly. If you need/want
24948      * custom HTML cleanup, this is the method you should override.
24949      * @param {String} html The HTML to be cleaned
24950      * return {String} The cleaned HTML
24951      */
24952     cleanHtml : function(html){
24953         html = String(html);
24954         if(html.length > 5){
24955             if(Roo.isSafari){ // strip safari nonsense
24956                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24957             }
24958         }
24959         if(html == '&nbsp;'){
24960             html = '';
24961         }
24962         return html;
24963     },
24964
24965     /**
24966      * HTML Editor -> Textarea
24967      * Protected method that will not generally be called directly. Syncs the contents
24968      * of the editor iframe with the textarea.
24969      */
24970     syncValue : function(){
24971         if(this.initialized){
24972             var bd = (this.doc.body || this.doc.documentElement);
24973             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24974             var html = bd.innerHTML;
24975             if(Roo.isSafari){
24976                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24977                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24978                 if(m && m[1]){
24979                     html = '<div style="'+m[0]+'">' + html + '</div>';
24980                 }
24981             }
24982             html = this.cleanHtml(html);
24983             // fix up the special chars.. normaly like back quotes in word...
24984             // however we do not want to do this with chinese..
24985             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24986                 var cc = b.charCodeAt();
24987                 if (
24988                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24989                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24990                     (cc >= 0xf900 && cc < 0xfb00 )
24991                 ) {
24992                         return b;
24993                 }
24994                 return "&#"+cc+";" 
24995             });
24996             if(this.owner.fireEvent('beforesync', this, html) !== false){
24997                 this.el.dom.value = html;
24998                 this.owner.fireEvent('sync', this, html);
24999             }
25000         }
25001     },
25002
25003     /**
25004      * Protected method that will not generally be called directly. Pushes the value of the textarea
25005      * into the iframe editor.
25006      */
25007     pushValue : function(){
25008         if(this.initialized){
25009             var v = this.el.dom.value.trim();
25010             
25011 //            if(v.length < 1){
25012 //                v = '&#160;';
25013 //            }
25014             
25015             if(this.owner.fireEvent('beforepush', this, v) !== false){
25016                 var d = (this.doc.body || this.doc.documentElement);
25017                 d.innerHTML = v;
25018                 this.cleanUpPaste();
25019                 this.el.dom.value = d.innerHTML;
25020                 this.owner.fireEvent('push', this, v);
25021             }
25022         }
25023     },
25024
25025     // private
25026     deferFocus : function(){
25027         this.focus.defer(10, this);
25028     },
25029
25030     // doc'ed in Field
25031     focus : function(){
25032         if(this.win && !this.sourceEditMode){
25033             this.win.focus();
25034         }else{
25035             this.el.focus();
25036         }
25037     },
25038     
25039     assignDocWin: function()
25040     {
25041         var iframe = this.iframe;
25042         
25043          if(Roo.isIE){
25044             this.doc = iframe.contentWindow.document;
25045             this.win = iframe.contentWindow;
25046         } else {
25047 //            if (!Roo.get(this.frameId)) {
25048 //                return;
25049 //            }
25050 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25051 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25052             
25053             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25054                 return;
25055             }
25056             
25057             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25058             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25059         }
25060     },
25061     
25062     // private
25063     initEditor : function(){
25064         //console.log("INIT EDITOR");
25065         this.assignDocWin();
25066         
25067         
25068         
25069         this.doc.designMode="on";
25070         this.doc.open();
25071         this.doc.write(this.getDocMarkup());
25072         this.doc.close();
25073         
25074         var dbody = (this.doc.body || this.doc.documentElement);
25075         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25076         // this copies styles from the containing element into thsi one..
25077         // not sure why we need all of this..
25078         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25079         
25080         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25081         //ss['background-attachment'] = 'fixed'; // w3c
25082         dbody.bgProperties = 'fixed'; // ie
25083         //Roo.DomHelper.applyStyles(dbody, ss);
25084         Roo.EventManager.on(this.doc, {
25085             //'mousedown': this.onEditorEvent,
25086             'mouseup': this.onEditorEvent,
25087             'dblclick': this.onEditorEvent,
25088             'click': this.onEditorEvent,
25089             'keyup': this.onEditorEvent,
25090             buffer:100,
25091             scope: this
25092         });
25093         if(Roo.isGecko){
25094             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25095         }
25096         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25097             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25098         }
25099         this.initialized = true;
25100
25101         this.owner.fireEvent('initialize', this);
25102         this.pushValue();
25103     },
25104
25105     // private
25106     onDestroy : function(){
25107         
25108         
25109         
25110         if(this.rendered){
25111             
25112             //for (var i =0; i < this.toolbars.length;i++) {
25113             //    // fixme - ask toolbars for heights?
25114             //    this.toolbars[i].onDestroy();
25115            // }
25116             
25117             //this.wrap.dom.innerHTML = '';
25118             //this.wrap.remove();
25119         }
25120     },
25121
25122     // private
25123     onFirstFocus : function(){
25124         
25125         this.assignDocWin();
25126         
25127         
25128         this.activated = true;
25129          
25130     
25131         if(Roo.isGecko){ // prevent silly gecko errors
25132             this.win.focus();
25133             var s = this.win.getSelection();
25134             if(!s.focusNode || s.focusNode.nodeType != 3){
25135                 var r = s.getRangeAt(0);
25136                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25137                 r.collapse(true);
25138                 this.deferFocus();
25139             }
25140             try{
25141                 this.execCmd('useCSS', true);
25142                 this.execCmd('styleWithCSS', false);
25143             }catch(e){}
25144         }
25145         this.owner.fireEvent('activate', this);
25146     },
25147
25148     // private
25149     adjustFont: function(btn){
25150         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25151         //if(Roo.isSafari){ // safari
25152         //    adjust *= 2;
25153        // }
25154         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25155         if(Roo.isSafari){ // safari
25156             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25157             v =  (v < 10) ? 10 : v;
25158             v =  (v > 48) ? 48 : v;
25159             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25160             
25161         }
25162         
25163         
25164         v = Math.max(1, v+adjust);
25165         
25166         this.execCmd('FontSize', v  );
25167     },
25168
25169     onEditorEvent : function(e)
25170     {
25171         this.owner.fireEvent('editorevent', this, e);
25172       //  this.updateToolbar();
25173         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25174     },
25175
25176     insertTag : function(tg)
25177     {
25178         // could be a bit smarter... -> wrap the current selected tRoo..
25179         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25180             
25181             range = this.createRange(this.getSelection());
25182             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25183             wrappingNode.appendChild(range.extractContents());
25184             range.insertNode(wrappingNode);
25185
25186             return;
25187             
25188             
25189             
25190         }
25191         this.execCmd("formatblock",   tg);
25192         
25193     },
25194     
25195     insertText : function(txt)
25196     {
25197         
25198         
25199         var range = this.createRange();
25200         range.deleteContents();
25201                //alert(Sender.getAttribute('label'));
25202                
25203         range.insertNode(this.doc.createTextNode(txt));
25204     } ,
25205     
25206      
25207
25208     /**
25209      * Executes a Midas editor command on the editor document and performs necessary focus and
25210      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25211      * @param {String} cmd The Midas command
25212      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25213      */
25214     relayCmd : function(cmd, value){
25215         this.win.focus();
25216         this.execCmd(cmd, value);
25217         this.owner.fireEvent('editorevent', this);
25218         //this.updateToolbar();
25219         this.owner.deferFocus();
25220     },
25221
25222     /**
25223      * Executes a Midas editor command directly on the editor document.
25224      * For visual commands, you should use {@link #relayCmd} instead.
25225      * <b>This should only be called after the editor is initialized.</b>
25226      * @param {String} cmd The Midas command
25227      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25228      */
25229     execCmd : function(cmd, value){
25230         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25231         this.syncValue();
25232     },
25233  
25234  
25235    
25236     /**
25237      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25238      * to insert tRoo.
25239      * @param {String} text | dom node.. 
25240      */
25241     insertAtCursor : function(text)
25242     {
25243         
25244         
25245         
25246         if(!this.activated){
25247             return;
25248         }
25249         /*
25250         if(Roo.isIE){
25251             this.win.focus();
25252             var r = this.doc.selection.createRange();
25253             if(r){
25254                 r.collapse(true);
25255                 r.pasteHTML(text);
25256                 this.syncValue();
25257                 this.deferFocus();
25258             
25259             }
25260             return;
25261         }
25262         */
25263         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25264             this.win.focus();
25265             
25266             
25267             // from jquery ui (MIT licenced)
25268             var range, node;
25269             var win = this.win;
25270             
25271             if (win.getSelection && win.getSelection().getRangeAt) {
25272                 range = win.getSelection().getRangeAt(0);
25273                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25274                 range.insertNode(node);
25275             } else if (win.document.selection && win.document.selection.createRange) {
25276                 // no firefox support
25277                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25278                 win.document.selection.createRange().pasteHTML(txt);
25279             } else {
25280                 // no firefox support
25281                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25282                 this.execCmd('InsertHTML', txt);
25283             } 
25284             
25285             this.syncValue();
25286             
25287             this.deferFocus();
25288         }
25289     },
25290  // private
25291     mozKeyPress : function(e){
25292         if(e.ctrlKey){
25293             var c = e.getCharCode(), cmd;
25294           
25295             if(c > 0){
25296                 c = String.fromCharCode(c).toLowerCase();
25297                 switch(c){
25298                     case 'b':
25299                         cmd = 'bold';
25300                         break;
25301                     case 'i':
25302                         cmd = 'italic';
25303                         break;
25304                     
25305                     case 'u':
25306                         cmd = 'underline';
25307                         break;
25308                     
25309                     case 'v':
25310                         this.cleanUpPaste.defer(100, this);
25311                         return;
25312                         
25313                 }
25314                 if(cmd){
25315                     this.win.focus();
25316                     this.execCmd(cmd);
25317                     this.deferFocus();
25318                     e.preventDefault();
25319                 }
25320                 
25321             }
25322         }
25323     },
25324
25325     // private
25326     fixKeys : function(){ // load time branching for fastest keydown performance
25327         if(Roo.isIE){
25328             return function(e){
25329                 var k = e.getKey(), r;
25330                 if(k == e.TAB){
25331                     e.stopEvent();
25332                     r = this.doc.selection.createRange();
25333                     if(r){
25334                         r.collapse(true);
25335                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25336                         this.deferFocus();
25337                     }
25338                     return;
25339                 }
25340                 
25341                 if(k == e.ENTER){
25342                     r = this.doc.selection.createRange();
25343                     if(r){
25344                         var target = r.parentElement();
25345                         if(!target || target.tagName.toLowerCase() != 'li'){
25346                             e.stopEvent();
25347                             r.pasteHTML('<br />');
25348                             r.collapse(false);
25349                             r.select();
25350                         }
25351                     }
25352                 }
25353                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25354                     this.cleanUpPaste.defer(100, this);
25355                     return;
25356                 }
25357                 
25358                 
25359             };
25360         }else if(Roo.isOpera){
25361             return function(e){
25362                 var k = e.getKey();
25363                 if(k == e.TAB){
25364                     e.stopEvent();
25365                     this.win.focus();
25366                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25367                     this.deferFocus();
25368                 }
25369                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25370                     this.cleanUpPaste.defer(100, this);
25371                     return;
25372                 }
25373                 
25374             };
25375         }else if(Roo.isSafari){
25376             return function(e){
25377                 var k = e.getKey();
25378                 
25379                 if(k == e.TAB){
25380                     e.stopEvent();
25381                     this.execCmd('InsertText','\t');
25382                     this.deferFocus();
25383                     return;
25384                 }
25385                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25386                     this.cleanUpPaste.defer(100, this);
25387                     return;
25388                 }
25389                 
25390              };
25391         }
25392     }(),
25393     
25394     getAllAncestors: function()
25395     {
25396         var p = this.getSelectedNode();
25397         var a = [];
25398         if (!p) {
25399             a.push(p); // push blank onto stack..
25400             p = this.getParentElement();
25401         }
25402         
25403         
25404         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25405             a.push(p);
25406             p = p.parentNode;
25407         }
25408         a.push(this.doc.body);
25409         return a;
25410     },
25411     lastSel : false,
25412     lastSelNode : false,
25413     
25414     
25415     getSelection : function() 
25416     {
25417         this.assignDocWin();
25418         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25419     },
25420     
25421     getSelectedNode: function() 
25422     {
25423         // this may only work on Gecko!!!
25424         
25425         // should we cache this!!!!
25426         
25427         
25428         
25429          
25430         var range = this.createRange(this.getSelection()).cloneRange();
25431         
25432         if (Roo.isIE) {
25433             var parent = range.parentElement();
25434             while (true) {
25435                 var testRange = range.duplicate();
25436                 testRange.moveToElementText(parent);
25437                 if (testRange.inRange(range)) {
25438                     break;
25439                 }
25440                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25441                     break;
25442                 }
25443                 parent = parent.parentElement;
25444             }
25445             return parent;
25446         }
25447         
25448         // is ancestor a text element.
25449         var ac =  range.commonAncestorContainer;
25450         if (ac.nodeType == 3) {
25451             ac = ac.parentNode;
25452         }
25453         
25454         var ar = ac.childNodes;
25455          
25456         var nodes = [];
25457         var other_nodes = [];
25458         var has_other_nodes = false;
25459         for (var i=0;i<ar.length;i++) {
25460             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25461                 continue;
25462             }
25463             // fullly contained node.
25464             
25465             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25466                 nodes.push(ar[i]);
25467                 continue;
25468             }
25469             
25470             // probably selected..
25471             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25472                 other_nodes.push(ar[i]);
25473                 continue;
25474             }
25475             // outer..
25476             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25477                 continue;
25478             }
25479             
25480             
25481             has_other_nodes = true;
25482         }
25483         if (!nodes.length && other_nodes.length) {
25484             nodes= other_nodes;
25485         }
25486         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25487             return false;
25488         }
25489         
25490         return nodes[0];
25491     },
25492     createRange: function(sel)
25493     {
25494         // this has strange effects when using with 
25495         // top toolbar - not sure if it's a great idea.
25496         //this.editor.contentWindow.focus();
25497         if (typeof sel != "undefined") {
25498             try {
25499                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25500             } catch(e) {
25501                 return this.doc.createRange();
25502             }
25503         } else {
25504             return this.doc.createRange();
25505         }
25506     },
25507     getParentElement: function()
25508     {
25509         
25510         this.assignDocWin();
25511         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25512         
25513         var range = this.createRange(sel);
25514          
25515         try {
25516             var p = range.commonAncestorContainer;
25517             while (p.nodeType == 3) { // text node
25518                 p = p.parentNode;
25519             }
25520             return p;
25521         } catch (e) {
25522             return null;
25523         }
25524     
25525     },
25526     /***
25527      *
25528      * Range intersection.. the hard stuff...
25529      *  '-1' = before
25530      *  '0' = hits..
25531      *  '1' = after.
25532      *         [ -- selected range --- ]
25533      *   [fail]                        [fail]
25534      *
25535      *    basically..
25536      *      if end is before start or  hits it. fail.
25537      *      if start is after end or hits it fail.
25538      *
25539      *   if either hits (but other is outside. - then it's not 
25540      *   
25541      *    
25542      **/
25543     
25544     
25545     // @see http://www.thismuchiknow.co.uk/?p=64.
25546     rangeIntersectsNode : function(range, node)
25547     {
25548         var nodeRange = node.ownerDocument.createRange();
25549         try {
25550             nodeRange.selectNode(node);
25551         } catch (e) {
25552             nodeRange.selectNodeContents(node);
25553         }
25554     
25555         var rangeStartRange = range.cloneRange();
25556         rangeStartRange.collapse(true);
25557     
25558         var rangeEndRange = range.cloneRange();
25559         rangeEndRange.collapse(false);
25560     
25561         var nodeStartRange = nodeRange.cloneRange();
25562         nodeStartRange.collapse(true);
25563     
25564         var nodeEndRange = nodeRange.cloneRange();
25565         nodeEndRange.collapse(false);
25566     
25567         return rangeStartRange.compareBoundaryPoints(
25568                  Range.START_TO_START, nodeEndRange) == -1 &&
25569                rangeEndRange.compareBoundaryPoints(
25570                  Range.START_TO_START, nodeStartRange) == 1;
25571         
25572          
25573     },
25574     rangeCompareNode : function(range, node)
25575     {
25576         var nodeRange = node.ownerDocument.createRange();
25577         try {
25578             nodeRange.selectNode(node);
25579         } catch (e) {
25580             nodeRange.selectNodeContents(node);
25581         }
25582         
25583         
25584         range.collapse(true);
25585     
25586         nodeRange.collapse(true);
25587      
25588         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25589         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25590          
25591         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25592         
25593         var nodeIsBefore   =  ss == 1;
25594         var nodeIsAfter    = ee == -1;
25595         
25596         if (nodeIsBefore && nodeIsAfter)
25597             return 0; // outer
25598         if (!nodeIsBefore && nodeIsAfter)
25599             return 1; //right trailed.
25600         
25601         if (nodeIsBefore && !nodeIsAfter)
25602             return 2;  // left trailed.
25603         // fully contined.
25604         return 3;
25605     },
25606
25607     // private? - in a new class?
25608     cleanUpPaste :  function()
25609     {
25610         // cleans up the whole document..
25611         Roo.log('cleanuppaste');
25612         
25613         this.cleanUpChildren(this.doc.body);
25614         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25615         if (clean != this.doc.body.innerHTML) {
25616             this.doc.body.innerHTML = clean;
25617         }
25618         
25619     },
25620     
25621     cleanWordChars : function(input) {// change the chars to hex code
25622         var he = Roo.HtmlEditorCore;
25623         
25624         var output = input;
25625         Roo.each(he.swapCodes, function(sw) { 
25626             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25627             
25628             output = output.replace(swapper, sw[1]);
25629         });
25630         
25631         return output;
25632     },
25633     
25634     
25635     cleanUpChildren : function (n)
25636     {
25637         if (!n.childNodes.length) {
25638             return;
25639         }
25640         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25641            this.cleanUpChild(n.childNodes[i]);
25642         }
25643     },
25644     
25645     
25646         
25647     
25648     cleanUpChild : function (node)
25649     {
25650         var ed = this;
25651         //console.log(node);
25652         if (node.nodeName == "#text") {
25653             // clean up silly Windows -- stuff?
25654             return; 
25655         }
25656         if (node.nodeName == "#comment") {
25657             node.parentNode.removeChild(node);
25658             // clean up silly Windows -- stuff?
25659             return; 
25660         }
25661         var lcname = node.tagName.toLowerCase();
25662         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25663         // whitelist of tags..
25664         
25665         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25666             // remove node.
25667             node.parentNode.removeChild(node);
25668             return;
25669             
25670         }
25671         
25672         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25673         
25674         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25675         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25676         
25677         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25678         //    remove_keep_children = true;
25679         //}
25680         
25681         if (remove_keep_children) {
25682             this.cleanUpChildren(node);
25683             // inserts everything just before this node...
25684             while (node.childNodes.length) {
25685                 var cn = node.childNodes[0];
25686                 node.removeChild(cn);
25687                 node.parentNode.insertBefore(cn, node);
25688             }
25689             node.parentNode.removeChild(node);
25690             return;
25691         }
25692         
25693         if (!node.attributes || !node.attributes.length) {
25694             this.cleanUpChildren(node);
25695             return;
25696         }
25697         
25698         function cleanAttr(n,v)
25699         {
25700             
25701             if (v.match(/^\./) || v.match(/^\//)) {
25702                 return;
25703             }
25704             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25705                 return;
25706             }
25707             if (v.match(/^#/)) {
25708                 return;
25709             }
25710 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25711             node.removeAttribute(n);
25712             
25713         }
25714         
25715         var cwhite = this.cwhite;
25716         var cblack = this.cblack;
25717             
25718         function cleanStyle(n,v)
25719         {
25720             if (v.match(/expression/)) { //XSS?? should we even bother..
25721                 node.removeAttribute(n);
25722                 return;
25723             }
25724             
25725             var parts = v.split(/;/);
25726             var clean = [];
25727             
25728             Roo.each(parts, function(p) {
25729                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25730                 if (!p.length) {
25731                     return true;
25732                 }
25733                 var l = p.split(':').shift().replace(/\s+/g,'');
25734                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25735                 
25736                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25737 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25738                     //node.removeAttribute(n);
25739                     return true;
25740                 }
25741                 //Roo.log()
25742                 // only allow 'c whitelisted system attributes'
25743                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25744 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25745                     //node.removeAttribute(n);
25746                     return true;
25747                 }
25748                 
25749                 
25750                  
25751                 
25752                 clean.push(p);
25753                 return true;
25754             });
25755             if (clean.length) { 
25756                 node.setAttribute(n, clean.join(';'));
25757             } else {
25758                 node.removeAttribute(n);
25759             }
25760             
25761         }
25762         
25763         
25764         for (var i = node.attributes.length-1; i > -1 ; i--) {
25765             var a = node.attributes[i];
25766             //console.log(a);
25767             
25768             if (a.name.toLowerCase().substr(0,2)=='on')  {
25769                 node.removeAttribute(a.name);
25770                 continue;
25771             }
25772             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25773                 node.removeAttribute(a.name);
25774                 continue;
25775             }
25776             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25777                 cleanAttr(a.name,a.value); // fixme..
25778                 continue;
25779             }
25780             if (a.name == 'style') {
25781                 cleanStyle(a.name,a.value);
25782                 continue;
25783             }
25784             /// clean up MS crap..
25785             // tecnically this should be a list of valid class'es..
25786             
25787             
25788             if (a.name == 'class') {
25789                 if (a.value.match(/^Mso/)) {
25790                     node.className = '';
25791                 }
25792                 
25793                 if (a.value.match(/body/)) {
25794                     node.className = '';
25795                 }
25796                 continue;
25797             }
25798             
25799             // style cleanup!?
25800             // class cleanup?
25801             
25802         }
25803         
25804         
25805         this.cleanUpChildren(node);
25806         
25807         
25808     },
25809     
25810     /**
25811      * Clean up MS wordisms...
25812      */
25813     cleanWord : function(node)
25814     {
25815         
25816         
25817         if (!node) {
25818             this.cleanWord(this.doc.body);
25819             return;
25820         }
25821         if (node.nodeName == "#text") {
25822             // clean up silly Windows -- stuff?
25823             return; 
25824         }
25825         if (node.nodeName == "#comment") {
25826             node.parentNode.removeChild(node);
25827             // clean up silly Windows -- stuff?
25828             return; 
25829         }
25830         
25831         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25832             node.parentNode.removeChild(node);
25833             return;
25834         }
25835         
25836         // remove - but keep children..
25837         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25838             while (node.childNodes.length) {
25839                 var cn = node.childNodes[0];
25840                 node.removeChild(cn);
25841                 node.parentNode.insertBefore(cn, node);
25842             }
25843             node.parentNode.removeChild(node);
25844             this.iterateChildren(node, this.cleanWord);
25845             return;
25846         }
25847         // clean styles
25848         if (node.className.length) {
25849             
25850             var cn = node.className.split(/\W+/);
25851             var cna = [];
25852             Roo.each(cn, function(cls) {
25853                 if (cls.match(/Mso[a-zA-Z]+/)) {
25854                     return;
25855                 }
25856                 cna.push(cls);
25857             });
25858             node.className = cna.length ? cna.join(' ') : '';
25859             if (!cna.length) {
25860                 node.removeAttribute("class");
25861             }
25862         }
25863         
25864         if (node.hasAttribute("lang")) {
25865             node.removeAttribute("lang");
25866         }
25867         
25868         if (node.hasAttribute("style")) {
25869             
25870             var styles = node.getAttribute("style").split(";");
25871             var nstyle = [];
25872             Roo.each(styles, function(s) {
25873                 if (!s.match(/:/)) {
25874                     return;
25875                 }
25876                 var kv = s.split(":");
25877                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25878                     return;
25879                 }
25880                 // what ever is left... we allow.
25881                 nstyle.push(s);
25882             });
25883             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25884             if (!nstyle.length) {
25885                 node.removeAttribute('style');
25886             }
25887         }
25888         this.iterateChildren(node, this.cleanWord);
25889         
25890         
25891         
25892     },
25893     /**
25894      * iterateChildren of a Node, calling fn each time, using this as the scole..
25895      * @param {DomNode} node node to iterate children of.
25896      * @param {Function} fn method of this class to call on each item.
25897      */
25898     iterateChildren : function(node, fn)
25899     {
25900         if (!node.childNodes.length) {
25901                 return;
25902         }
25903         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25904            fn.call(this, node.childNodes[i])
25905         }
25906     },
25907     
25908     
25909     /**
25910      * cleanTableWidths.
25911      *
25912      * Quite often pasting from word etc.. results in tables with column and widths.
25913      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25914      *
25915      */
25916     cleanTableWidths : function(node)
25917     {
25918          
25919          
25920         if (!node) {
25921             this.cleanTableWidths(this.doc.body);
25922             return;
25923         }
25924         
25925         // ignore list...
25926         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25927             return; 
25928         }
25929         Roo.log(node.tagName);
25930         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25931             this.iterateChildren(node, this.cleanTableWidths);
25932             return;
25933         }
25934         if (node.hasAttribute('width')) {
25935             node.removeAttribute('width');
25936         }
25937         
25938          
25939         if (node.hasAttribute("style")) {
25940             // pretty basic...
25941             
25942             var styles = node.getAttribute("style").split(";");
25943             var nstyle = [];
25944             Roo.each(styles, function(s) {
25945                 if (!s.match(/:/)) {
25946                     return;
25947                 }
25948                 var kv = s.split(":");
25949                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25950                     return;
25951                 }
25952                 // what ever is left... we allow.
25953                 nstyle.push(s);
25954             });
25955             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25956             if (!nstyle.length) {
25957                 node.removeAttribute('style');
25958             }
25959         }
25960         
25961         this.iterateChildren(node, this.cleanTableWidths);
25962         
25963         
25964     },
25965     
25966     
25967     
25968     
25969     domToHTML : function(currentElement, depth, nopadtext) {
25970         
25971         depth = depth || 0;
25972         nopadtext = nopadtext || false;
25973     
25974         if (!currentElement) {
25975             return this.domToHTML(this.doc.body);
25976         }
25977         
25978         //Roo.log(currentElement);
25979         var j;
25980         var allText = false;
25981         var nodeName = currentElement.nodeName;
25982         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25983         
25984         if  (nodeName == '#text') {
25985             
25986             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25987         }
25988         
25989         
25990         var ret = '';
25991         if (nodeName != 'BODY') {
25992              
25993             var i = 0;
25994             // Prints the node tagName, such as <A>, <IMG>, etc
25995             if (tagName) {
25996                 var attr = [];
25997                 for(i = 0; i < currentElement.attributes.length;i++) {
25998                     // quoting?
25999                     var aname = currentElement.attributes.item(i).name;
26000                     if (!currentElement.attributes.item(i).value.length) {
26001                         continue;
26002                     }
26003                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26004                 }
26005                 
26006                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26007             } 
26008             else {
26009                 
26010                 // eack
26011             }
26012         } else {
26013             tagName = false;
26014         }
26015         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26016             return ret;
26017         }
26018         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26019             nopadtext = true;
26020         }
26021         
26022         
26023         // Traverse the tree
26024         i = 0;
26025         var currentElementChild = currentElement.childNodes.item(i);
26026         var allText = true;
26027         var innerHTML  = '';
26028         lastnode = '';
26029         while (currentElementChild) {
26030             // Formatting code (indent the tree so it looks nice on the screen)
26031             var nopad = nopadtext;
26032             if (lastnode == 'SPAN') {
26033                 nopad  = true;
26034             }
26035             // text
26036             if  (currentElementChild.nodeName == '#text') {
26037                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26038                 toadd = nopadtext ? toadd : toadd.trim();
26039                 if (!nopad && toadd.length > 80) {
26040                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26041                 }
26042                 innerHTML  += toadd;
26043                 
26044                 i++;
26045                 currentElementChild = currentElement.childNodes.item(i);
26046                 lastNode = '';
26047                 continue;
26048             }
26049             allText = false;
26050             
26051             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26052                 
26053             // Recursively traverse the tree structure of the child node
26054             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26055             lastnode = currentElementChild.nodeName;
26056             i++;
26057             currentElementChild=currentElement.childNodes.item(i);
26058         }
26059         
26060         ret += innerHTML;
26061         
26062         if (!allText) {
26063                 // The remaining code is mostly for formatting the tree
26064             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26065         }
26066         
26067         
26068         if (tagName) {
26069             ret+= "</"+tagName+">";
26070         }
26071         return ret;
26072         
26073     },
26074         
26075     applyBlacklists : function()
26076     {
26077         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26078         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26079         
26080         this.white = [];
26081         this.black = [];
26082         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26083             if (b.indexOf(tag) > -1) {
26084                 return;
26085             }
26086             this.white.push(tag);
26087             
26088         }, this);
26089         
26090         Roo.each(w, function(tag) {
26091             if (b.indexOf(tag) > -1) {
26092                 return;
26093             }
26094             if (this.white.indexOf(tag) > -1) {
26095                 return;
26096             }
26097             this.white.push(tag);
26098             
26099         }, this);
26100         
26101         
26102         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26103             if (w.indexOf(tag) > -1) {
26104                 return;
26105             }
26106             this.black.push(tag);
26107             
26108         }, this);
26109         
26110         Roo.each(b, function(tag) {
26111             if (w.indexOf(tag) > -1) {
26112                 return;
26113             }
26114             if (this.black.indexOf(tag) > -1) {
26115                 return;
26116             }
26117             this.black.push(tag);
26118             
26119         }, this);
26120         
26121         
26122         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26123         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26124         
26125         this.cwhite = [];
26126         this.cblack = [];
26127         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26128             if (b.indexOf(tag) > -1) {
26129                 return;
26130             }
26131             this.cwhite.push(tag);
26132             
26133         }, this);
26134         
26135         Roo.each(w, function(tag) {
26136             if (b.indexOf(tag) > -1) {
26137                 return;
26138             }
26139             if (this.cwhite.indexOf(tag) > -1) {
26140                 return;
26141             }
26142             this.cwhite.push(tag);
26143             
26144         }, this);
26145         
26146         
26147         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26148             if (w.indexOf(tag) > -1) {
26149                 return;
26150             }
26151             this.cblack.push(tag);
26152             
26153         }, this);
26154         
26155         Roo.each(b, function(tag) {
26156             if (w.indexOf(tag) > -1) {
26157                 return;
26158             }
26159             if (this.cblack.indexOf(tag) > -1) {
26160                 return;
26161             }
26162             this.cblack.push(tag);
26163             
26164         }, this);
26165     },
26166     
26167     setStylesheets : function(stylesheets)
26168     {
26169         if(typeof(stylesheets) == 'string'){
26170             Roo.get(this.iframe.contentDocument.head).createChild({
26171                 tag : 'link',
26172                 rel : 'stylesheet',
26173                 type : 'text/css',
26174                 href : stylesheets
26175             });
26176             
26177             return;
26178         }
26179         var _this = this;
26180      
26181         Roo.each(stylesheets, function(s) {
26182             if(!s.length){
26183                 return;
26184             }
26185             
26186             Roo.get(_this.iframe.contentDocument.head).createChild({
26187                 tag : 'link',
26188                 rel : 'stylesheet',
26189                 type : 'text/css',
26190                 href : s
26191             });
26192         });
26193
26194         
26195     },
26196     
26197     removeStylesheets : function()
26198     {
26199         var _this = this;
26200         
26201         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26202             s.remove();
26203         });
26204     }
26205     
26206     // hide stuff that is not compatible
26207     /**
26208      * @event blur
26209      * @hide
26210      */
26211     /**
26212      * @event change
26213      * @hide
26214      */
26215     /**
26216      * @event focus
26217      * @hide
26218      */
26219     /**
26220      * @event specialkey
26221      * @hide
26222      */
26223     /**
26224      * @cfg {String} fieldClass @hide
26225      */
26226     /**
26227      * @cfg {String} focusClass @hide
26228      */
26229     /**
26230      * @cfg {String} autoCreate @hide
26231      */
26232     /**
26233      * @cfg {String} inputType @hide
26234      */
26235     /**
26236      * @cfg {String} invalidClass @hide
26237      */
26238     /**
26239      * @cfg {String} invalidText @hide
26240      */
26241     /**
26242      * @cfg {String} msgFx @hide
26243      */
26244     /**
26245      * @cfg {String} validateOnBlur @hide
26246      */
26247 });
26248
26249 Roo.HtmlEditorCore.white = [
26250         'area', 'br', 'img', 'input', 'hr', 'wbr',
26251         
26252        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26253        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26254        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26255        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26256        'table',   'ul',         'xmp', 
26257        
26258        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26259       'thead',   'tr', 
26260      
26261       'dir', 'menu', 'ol', 'ul', 'dl',
26262        
26263       'embed',  'object'
26264 ];
26265
26266
26267 Roo.HtmlEditorCore.black = [
26268     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26269         'applet', // 
26270         'base',   'basefont', 'bgsound', 'blink',  'body', 
26271         'frame',  'frameset', 'head',    'html',   'ilayer', 
26272         'iframe', 'layer',  'link',     'meta',    'object',   
26273         'script', 'style' ,'title',  'xml' // clean later..
26274 ];
26275 Roo.HtmlEditorCore.clean = [
26276     'script', 'style', 'title', 'xml'
26277 ];
26278 Roo.HtmlEditorCore.remove = [
26279     'font'
26280 ];
26281 // attributes..
26282
26283 Roo.HtmlEditorCore.ablack = [
26284     'on'
26285 ];
26286     
26287 Roo.HtmlEditorCore.aclean = [ 
26288     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26289 ];
26290
26291 // protocols..
26292 Roo.HtmlEditorCore.pwhite= [
26293         'http',  'https',  'mailto'
26294 ];
26295
26296 // white listed style attributes.
26297 Roo.HtmlEditorCore.cwhite= [
26298       //  'text-align', /// default is to allow most things..
26299       
26300          
26301 //        'font-size'//??
26302 ];
26303
26304 // black listed style attributes.
26305 Roo.HtmlEditorCore.cblack= [
26306       //  'font-size' -- this can be set by the project 
26307 ];
26308
26309
26310 Roo.HtmlEditorCore.swapCodes   =[ 
26311     [    8211, "--" ], 
26312     [    8212, "--" ], 
26313     [    8216,  "'" ],  
26314     [    8217, "'" ],  
26315     [    8220, '"' ],  
26316     [    8221, '"' ],  
26317     [    8226, "*" ],  
26318     [    8230, "..." ]
26319 ]; 
26320
26321     //<script type="text/javascript">
26322
26323 /*
26324  * Ext JS Library 1.1.1
26325  * Copyright(c) 2006-2007, Ext JS, LLC.
26326  * Licence LGPL
26327  * 
26328  */
26329  
26330  
26331 Roo.form.HtmlEditor = function(config){
26332     
26333     
26334     
26335     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26336     
26337     if (!this.toolbars) {
26338         this.toolbars = [];
26339     }
26340     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26341     
26342     
26343 };
26344
26345 /**
26346  * @class Roo.form.HtmlEditor
26347  * @extends Roo.form.Field
26348  * Provides a lightweight HTML Editor component.
26349  *
26350  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26351  * 
26352  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26353  * supported by this editor.</b><br/><br/>
26354  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26355  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26356  */
26357 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26358     /**
26359      * @cfg {Boolean} clearUp
26360      */
26361     clearUp : true,
26362       /**
26363      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26364      */
26365     toolbars : false,
26366    
26367      /**
26368      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26369      *                        Roo.resizable.
26370      */
26371     resizable : false,
26372      /**
26373      * @cfg {Number} height (in pixels)
26374      */   
26375     height: 300,
26376    /**
26377      * @cfg {Number} width (in pixels)
26378      */   
26379     width: 500,
26380     
26381     /**
26382      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26383      * 
26384      */
26385     stylesheets: false,
26386     
26387     
26388      /**
26389      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26390      * 
26391      */
26392     cblack: false,
26393     /**
26394      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26395      * 
26396      */
26397     cwhite: false,
26398     
26399      /**
26400      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26401      * 
26402      */
26403     black: false,
26404     /**
26405      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26406      * 
26407      */
26408     white: false,
26409     
26410     // id of frame..
26411     frameId: false,
26412     
26413     // private properties
26414     validationEvent : false,
26415     deferHeight: true,
26416     initialized : false,
26417     activated : false,
26418     
26419     onFocus : Roo.emptyFn,
26420     iframePad:3,
26421     hideMode:'offsets',
26422     
26423     actionMode : 'container', // defaults to hiding it...
26424     
26425     defaultAutoCreate : { // modified by initCompnoent..
26426         tag: "textarea",
26427         style:"width:500px;height:300px;",
26428         autocomplete: "new-password"
26429     },
26430
26431     // private
26432     initComponent : function(){
26433         this.addEvents({
26434             /**
26435              * @event initialize
26436              * Fires when the editor is fully initialized (including the iframe)
26437              * @param {HtmlEditor} this
26438              */
26439             initialize: true,
26440             /**
26441              * @event activate
26442              * Fires when the editor is first receives the focus. Any insertion must wait
26443              * until after this event.
26444              * @param {HtmlEditor} this
26445              */
26446             activate: true,
26447              /**
26448              * @event beforesync
26449              * Fires before the textarea is updated with content from the editor iframe. Return false
26450              * to cancel the sync.
26451              * @param {HtmlEditor} this
26452              * @param {String} html
26453              */
26454             beforesync: true,
26455              /**
26456              * @event beforepush
26457              * Fires before the iframe editor is updated with content from the textarea. Return false
26458              * to cancel the push.
26459              * @param {HtmlEditor} this
26460              * @param {String} html
26461              */
26462             beforepush: true,
26463              /**
26464              * @event sync
26465              * Fires when the textarea is updated with content from the editor iframe.
26466              * @param {HtmlEditor} this
26467              * @param {String} html
26468              */
26469             sync: true,
26470              /**
26471              * @event push
26472              * Fires when the iframe editor is updated with content from the textarea.
26473              * @param {HtmlEditor} this
26474              * @param {String} html
26475              */
26476             push: true,
26477              /**
26478              * @event editmodechange
26479              * Fires when the editor switches edit modes
26480              * @param {HtmlEditor} this
26481              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26482              */
26483             editmodechange: true,
26484             /**
26485              * @event editorevent
26486              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26487              * @param {HtmlEditor} this
26488              */
26489             editorevent: true,
26490             /**
26491              * @event firstfocus
26492              * Fires when on first focus - needed by toolbars..
26493              * @param {HtmlEditor} this
26494              */
26495             firstfocus: true,
26496             /**
26497              * @event autosave
26498              * Auto save the htmlEditor value as a file into Events
26499              * @param {HtmlEditor} this
26500              */
26501             autosave: true,
26502             /**
26503              * @event savedpreview
26504              * preview the saved version of htmlEditor
26505              * @param {HtmlEditor} this
26506              */
26507             savedpreview: true,
26508             
26509             /**
26510             * @event stylesheetsclick
26511             * Fires when press the Sytlesheets button
26512             * @param {Roo.HtmlEditorCore} this
26513             */
26514             stylesheetsclick: true
26515         });
26516         this.defaultAutoCreate =  {
26517             tag: "textarea",
26518             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26519             autocomplete: "new-password"
26520         };
26521     },
26522
26523     /**
26524      * Protected method that will not generally be called directly. It
26525      * is called when the editor creates its toolbar. Override this method if you need to
26526      * add custom toolbar buttons.
26527      * @param {HtmlEditor} editor
26528      */
26529     createToolbar : function(editor){
26530         Roo.log("create toolbars");
26531         if (!editor.toolbars || !editor.toolbars.length) {
26532             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26533         }
26534         
26535         for (var i =0 ; i < editor.toolbars.length;i++) {
26536             editor.toolbars[i] = Roo.factory(
26537                     typeof(editor.toolbars[i]) == 'string' ?
26538                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26539                 Roo.form.HtmlEditor);
26540             editor.toolbars[i].init(editor);
26541         }
26542          
26543         
26544     },
26545
26546      
26547     // private
26548     onRender : function(ct, position)
26549     {
26550         var _t = this;
26551         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26552         
26553         this.wrap = this.el.wrap({
26554             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26555         });
26556         
26557         this.editorcore.onRender(ct, position);
26558          
26559         if (this.resizable) {
26560             this.resizeEl = new Roo.Resizable(this.wrap, {
26561                 pinned : true,
26562                 wrap: true,
26563                 dynamic : true,
26564                 minHeight : this.height,
26565                 height: this.height,
26566                 handles : this.resizable,
26567                 width: this.width,
26568                 listeners : {
26569                     resize : function(r, w, h) {
26570                         _t.onResize(w,h); // -something
26571                     }
26572                 }
26573             });
26574             
26575         }
26576         this.createToolbar(this);
26577        
26578         
26579         if(!this.width){
26580             this.setSize(this.wrap.getSize());
26581         }
26582         if (this.resizeEl) {
26583             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26584             // should trigger onReize..
26585         }
26586         
26587         this.keyNav = new Roo.KeyNav(this.el, {
26588             
26589             "tab" : function(e){
26590                 e.preventDefault();
26591                 
26592                 var value = this.getValue();
26593                 
26594                 var start = this.el.dom.selectionStart;
26595                 var end = this.el.dom.selectionEnd;
26596                 
26597                 if(!e.shiftKey){
26598                     
26599                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26600                     this.el.dom.setSelectionRange(end + 1, end + 1);
26601                     return;
26602                 }
26603                 
26604                 var f = value.substring(0, start).split("\t");
26605                 
26606                 if(f.pop().length != 0){
26607                     return;
26608                 }
26609                 
26610                 this.setValue(f.join("\t") + value.substring(end));
26611                 this.el.dom.setSelectionRange(start - 1, start - 1);
26612                 
26613             },
26614             
26615             "home" : function(e){
26616                 e.preventDefault();
26617                 
26618                 var curr = this.el.dom.selectionStart;
26619                 var lines = this.getValue().split("\n");
26620                 
26621                 if(!lines.length){
26622                     return;
26623                 }
26624                 
26625                 if(e.ctrlKey){
26626                     this.el.dom.setSelectionRange(0, 0);
26627                     return;
26628                 }
26629                 
26630                 var pos = 0;
26631                 
26632                 for (var i = 0; i < lines.length;i++) {
26633                     pos += lines[i].length;
26634                     
26635                     if(i != 0){
26636                         pos += 1;
26637                     }
26638                     
26639                     if(pos < curr){
26640                         continue;
26641                     }
26642                     
26643                     pos -= lines[i].length;
26644                     
26645                     break;
26646                 }
26647                 
26648                 if(!e.shiftKey){
26649                     this.el.dom.setSelectionRange(pos, pos);
26650                     return;
26651                 }
26652                 
26653                 this.el.dom.selectionStart = pos;
26654                 this.el.dom.selectionEnd = curr;
26655             },
26656             
26657             "end" : function(e){
26658                 e.preventDefault();
26659                 
26660                 var curr = this.el.dom.selectionStart;
26661                 var lines = this.getValue().split("\n");
26662                 
26663                 if(!lines.length){
26664                     return;
26665                 }
26666                 
26667                 if(e.ctrlKey){
26668                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26669                     return;
26670                 }
26671                 
26672                 var pos = 0;
26673                 
26674                 for (var i = 0; i < lines.length;i++) {
26675                     
26676                     pos += lines[i].length;
26677                     
26678                     if(i != 0){
26679                         pos += 1;
26680                     }
26681                     
26682                     if(pos < curr){
26683                         continue;
26684                     }
26685                     
26686                     break;
26687                 }
26688                 
26689                 if(!e.shiftKey){
26690                     this.el.dom.setSelectionRange(pos, pos);
26691                     return;
26692                 }
26693                 
26694                 this.el.dom.selectionStart = curr;
26695                 this.el.dom.selectionEnd = pos;
26696             },
26697
26698             scope : this,
26699
26700             doRelay : function(foo, bar, hname){
26701                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26702             },
26703
26704             forceKeyDown: true
26705         });
26706         
26707 //        if(this.autosave && this.w){
26708 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26709 //        }
26710     },
26711
26712     // private
26713     onResize : function(w, h)
26714     {
26715         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26716         var ew = false;
26717         var eh = false;
26718         
26719         if(this.el ){
26720             if(typeof w == 'number'){
26721                 var aw = w - this.wrap.getFrameWidth('lr');
26722                 this.el.setWidth(this.adjustWidth('textarea', aw));
26723                 ew = aw;
26724             }
26725             if(typeof h == 'number'){
26726                 var tbh = 0;
26727                 for (var i =0; i < this.toolbars.length;i++) {
26728                     // fixme - ask toolbars for heights?
26729                     tbh += this.toolbars[i].tb.el.getHeight();
26730                     if (this.toolbars[i].footer) {
26731                         tbh += this.toolbars[i].footer.el.getHeight();
26732                     }
26733                 }
26734                 
26735                 
26736                 
26737                 
26738                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26739                 ah -= 5; // knock a few pixes off for look..
26740 //                Roo.log(ah);
26741                 this.el.setHeight(this.adjustWidth('textarea', ah));
26742                 var eh = ah;
26743             }
26744         }
26745         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26746         this.editorcore.onResize(ew,eh);
26747         
26748     },
26749
26750     /**
26751      * Toggles the editor between standard and source edit mode.
26752      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26753      */
26754     toggleSourceEdit : function(sourceEditMode)
26755     {
26756         this.editorcore.toggleSourceEdit(sourceEditMode);
26757         
26758         if(this.editorcore.sourceEditMode){
26759             Roo.log('editor - showing textarea');
26760             
26761 //            Roo.log('in');
26762 //            Roo.log(this.syncValue());
26763             this.editorcore.syncValue();
26764             this.el.removeClass('x-hidden');
26765             this.el.dom.removeAttribute('tabIndex');
26766             this.el.focus();
26767             
26768             for (var i = 0; i < this.toolbars.length; i++) {
26769                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26770                     this.toolbars[i].tb.hide();
26771                     this.toolbars[i].footer.hide();
26772                 }
26773             }
26774             
26775         }else{
26776             Roo.log('editor - hiding textarea');
26777 //            Roo.log('out')
26778 //            Roo.log(this.pushValue()); 
26779             this.editorcore.pushValue();
26780             
26781             this.el.addClass('x-hidden');
26782             this.el.dom.setAttribute('tabIndex', -1);
26783             
26784             for (var i = 0; i < this.toolbars.length; i++) {
26785                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26786                     this.toolbars[i].tb.show();
26787                     this.toolbars[i].footer.show();
26788                 }
26789             }
26790             
26791             //this.deferFocus();
26792         }
26793         
26794         this.setSize(this.wrap.getSize());
26795         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26796         
26797         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26798     },
26799  
26800     // private (for BoxComponent)
26801     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26802
26803     // private (for BoxComponent)
26804     getResizeEl : function(){
26805         return this.wrap;
26806     },
26807
26808     // private (for BoxComponent)
26809     getPositionEl : function(){
26810         return this.wrap;
26811     },
26812
26813     // private
26814     initEvents : function(){
26815         this.originalValue = this.getValue();
26816     },
26817
26818     /**
26819      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26820      * @method
26821      */
26822     markInvalid : Roo.emptyFn,
26823     /**
26824      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26825      * @method
26826      */
26827     clearInvalid : Roo.emptyFn,
26828
26829     setValue : function(v){
26830         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26831         this.editorcore.pushValue();
26832     },
26833
26834      
26835     // private
26836     deferFocus : function(){
26837         this.focus.defer(10, this);
26838     },
26839
26840     // doc'ed in Field
26841     focus : function(){
26842         this.editorcore.focus();
26843         
26844     },
26845       
26846
26847     // private
26848     onDestroy : function(){
26849         
26850         
26851         
26852         if(this.rendered){
26853             
26854             for (var i =0; i < this.toolbars.length;i++) {
26855                 // fixme - ask toolbars for heights?
26856                 this.toolbars[i].onDestroy();
26857             }
26858             
26859             this.wrap.dom.innerHTML = '';
26860             this.wrap.remove();
26861         }
26862     },
26863
26864     // private
26865     onFirstFocus : function(){
26866         //Roo.log("onFirstFocus");
26867         this.editorcore.onFirstFocus();
26868          for (var i =0; i < this.toolbars.length;i++) {
26869             this.toolbars[i].onFirstFocus();
26870         }
26871         
26872     },
26873     
26874     // private
26875     syncValue : function()
26876     {
26877         this.editorcore.syncValue();
26878     },
26879     
26880     pushValue : function()
26881     {
26882         this.editorcore.pushValue();
26883     },
26884     
26885     setStylesheets : function(stylesheets)
26886     {
26887         this.editorcore.setStylesheets(stylesheets);
26888     },
26889     
26890     removeStylesheets : function()
26891     {
26892         this.editorcore.removeStylesheets();
26893     }
26894      
26895     
26896     // hide stuff that is not compatible
26897     /**
26898      * @event blur
26899      * @hide
26900      */
26901     /**
26902      * @event change
26903      * @hide
26904      */
26905     /**
26906      * @event focus
26907      * @hide
26908      */
26909     /**
26910      * @event specialkey
26911      * @hide
26912      */
26913     /**
26914      * @cfg {String} fieldClass @hide
26915      */
26916     /**
26917      * @cfg {String} focusClass @hide
26918      */
26919     /**
26920      * @cfg {String} autoCreate @hide
26921      */
26922     /**
26923      * @cfg {String} inputType @hide
26924      */
26925     /**
26926      * @cfg {String} invalidClass @hide
26927      */
26928     /**
26929      * @cfg {String} invalidText @hide
26930      */
26931     /**
26932      * @cfg {String} msgFx @hide
26933      */
26934     /**
26935      * @cfg {String} validateOnBlur @hide
26936      */
26937 });
26938  
26939     // <script type="text/javascript">
26940 /*
26941  * Based on
26942  * Ext JS Library 1.1.1
26943  * Copyright(c) 2006-2007, Ext JS, LLC.
26944  *  
26945  
26946  */
26947
26948 /**
26949  * @class Roo.form.HtmlEditorToolbar1
26950  * Basic Toolbar
26951  * 
26952  * Usage:
26953  *
26954  new Roo.form.HtmlEditor({
26955     ....
26956     toolbars : [
26957         new Roo.form.HtmlEditorToolbar1({
26958             disable : { fonts: 1 , format: 1, ..., ... , ...],
26959             btns : [ .... ]
26960         })
26961     }
26962      
26963  * 
26964  * @cfg {Object} disable List of elements to disable..
26965  * @cfg {Array} btns List of additional buttons.
26966  * 
26967  * 
26968  * NEEDS Extra CSS? 
26969  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26970  */
26971  
26972 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26973 {
26974     
26975     Roo.apply(this, config);
26976     
26977     // default disabled, based on 'good practice'..
26978     this.disable = this.disable || {};
26979     Roo.applyIf(this.disable, {
26980         fontSize : true,
26981         colors : true,
26982         specialElements : true
26983     });
26984     
26985     
26986     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26987     // dont call parent... till later.
26988 }
26989
26990 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26991     
26992     tb: false,
26993     
26994     rendered: false,
26995     
26996     editor : false,
26997     editorcore : false,
26998     /**
26999      * @cfg {Object} disable  List of toolbar elements to disable
27000          
27001      */
27002     disable : false,
27003     
27004     
27005      /**
27006      * @cfg {String} createLinkText The default text for the create link prompt
27007      */
27008     createLinkText : 'Please enter the URL for the link:',
27009     /**
27010      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27011      */
27012     defaultLinkValue : 'http:/'+'/',
27013    
27014     
27015       /**
27016      * @cfg {Array} fontFamilies An array of available font families
27017      */
27018     fontFamilies : [
27019         'Arial',
27020         'Courier New',
27021         'Tahoma',
27022         'Times New Roman',
27023         'Verdana'
27024     ],
27025     
27026     specialChars : [
27027            "&#169;",
27028           "&#174;",     
27029           "&#8482;",    
27030           "&#163;" ,    
27031          // "&#8212;",    
27032           "&#8230;",    
27033           "&#247;" ,    
27034         //  "&#225;" ,     ?? a acute?
27035            "&#8364;"    , //Euro
27036        //   "&#8220;"    ,
27037         //  "&#8221;"    ,
27038         //  "&#8226;"    ,
27039           "&#176;"  //   , // degrees
27040
27041          // "&#233;"     , // e ecute
27042          // "&#250;"     , // u ecute?
27043     ],
27044     
27045     specialElements : [
27046         {
27047             text: "Insert Table",
27048             xtype: 'MenuItem',
27049             xns : Roo.Menu,
27050             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27051                 
27052         },
27053         {    
27054             text: "Insert Image",
27055             xtype: 'MenuItem',
27056             xns : Roo.Menu,
27057             ihtml : '<img src="about:blank"/>'
27058             
27059         }
27060         
27061          
27062     ],
27063     
27064     
27065     inputElements : [ 
27066             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27067             "input:submit", "input:button", "select", "textarea", "label" ],
27068     formats : [
27069         ["p"] ,  
27070         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27071         ["pre"],[ "code"], 
27072         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27073         ['div'],['span']
27074     ],
27075     
27076     cleanStyles : [
27077         "font-size"
27078     ],
27079      /**
27080      * @cfg {String} defaultFont default font to use.
27081      */
27082     defaultFont: 'tahoma',
27083    
27084     fontSelect : false,
27085     
27086     
27087     formatCombo : false,
27088     
27089     init : function(editor)
27090     {
27091         this.editor = editor;
27092         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27093         var editorcore = this.editorcore;
27094         
27095         var _t = this;
27096         
27097         var fid = editorcore.frameId;
27098         var etb = this;
27099         function btn(id, toggle, handler){
27100             var xid = fid + '-'+ id ;
27101             return {
27102                 id : xid,
27103                 cmd : id,
27104                 cls : 'x-btn-icon x-edit-'+id,
27105                 enableToggle:toggle !== false,
27106                 scope: _t, // was editor...
27107                 handler:handler||_t.relayBtnCmd,
27108                 clickEvent:'mousedown',
27109                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27110                 tabIndex:-1
27111             };
27112         }
27113         
27114         
27115         
27116         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27117         this.tb = tb;
27118          // stop form submits
27119         tb.el.on('click', function(e){
27120             e.preventDefault(); // what does this do?
27121         });
27122
27123         if(!this.disable.font) { // && !Roo.isSafari){
27124             /* why no safari for fonts 
27125             editor.fontSelect = tb.el.createChild({
27126                 tag:'select',
27127                 tabIndex: -1,
27128                 cls:'x-font-select',
27129                 html: this.createFontOptions()
27130             });
27131             
27132             editor.fontSelect.on('change', function(){
27133                 var font = editor.fontSelect.dom.value;
27134                 editor.relayCmd('fontname', font);
27135                 editor.deferFocus();
27136             }, editor);
27137             
27138             tb.add(
27139                 editor.fontSelect.dom,
27140                 '-'
27141             );
27142             */
27143             
27144         };
27145         if(!this.disable.formats){
27146             this.formatCombo = new Roo.form.ComboBox({
27147                 store: new Roo.data.SimpleStore({
27148                     id : 'tag',
27149                     fields: ['tag'],
27150                     data : this.formats // from states.js
27151                 }),
27152                 blockFocus : true,
27153                 name : '',
27154                 //autoCreate : {tag: "div",  size: "20"},
27155                 displayField:'tag',
27156                 typeAhead: false,
27157                 mode: 'local',
27158                 editable : false,
27159                 triggerAction: 'all',
27160                 emptyText:'Add tag',
27161                 selectOnFocus:true,
27162                 width:135,
27163                 listeners : {
27164                     'select': function(c, r, i) {
27165                         editorcore.insertTag(r.get('tag'));
27166                         editor.focus();
27167                     }
27168                 }
27169
27170             });
27171             tb.addField(this.formatCombo);
27172             
27173         }
27174         
27175         if(!this.disable.format){
27176             tb.add(
27177                 btn('bold'),
27178                 btn('italic'),
27179                 btn('underline')
27180             );
27181         };
27182         if(!this.disable.fontSize){
27183             tb.add(
27184                 '-',
27185                 
27186                 
27187                 btn('increasefontsize', false, editorcore.adjustFont),
27188                 btn('decreasefontsize', false, editorcore.adjustFont)
27189             );
27190         };
27191         
27192         
27193         if(!this.disable.colors){
27194             tb.add(
27195                 '-', {
27196                     id:editorcore.frameId +'-forecolor',
27197                     cls:'x-btn-icon x-edit-forecolor',
27198                     clickEvent:'mousedown',
27199                     tooltip: this.buttonTips['forecolor'] || undefined,
27200                     tabIndex:-1,
27201                     menu : new Roo.menu.ColorMenu({
27202                         allowReselect: true,
27203                         focus: Roo.emptyFn,
27204                         value:'000000',
27205                         plain:true,
27206                         selectHandler: function(cp, color){
27207                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27208                             editor.deferFocus();
27209                         },
27210                         scope: editorcore,
27211                         clickEvent:'mousedown'
27212                     })
27213                 }, {
27214                     id:editorcore.frameId +'backcolor',
27215                     cls:'x-btn-icon x-edit-backcolor',
27216                     clickEvent:'mousedown',
27217                     tooltip: this.buttonTips['backcolor'] || undefined,
27218                     tabIndex:-1,
27219                     menu : new Roo.menu.ColorMenu({
27220                         focus: Roo.emptyFn,
27221                         value:'FFFFFF',
27222                         plain:true,
27223                         allowReselect: true,
27224                         selectHandler: function(cp, color){
27225                             if(Roo.isGecko){
27226                                 editorcore.execCmd('useCSS', false);
27227                                 editorcore.execCmd('hilitecolor', color);
27228                                 editorcore.execCmd('useCSS', true);
27229                                 editor.deferFocus();
27230                             }else{
27231                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27232                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27233                                 editor.deferFocus();
27234                             }
27235                         },
27236                         scope:editorcore,
27237                         clickEvent:'mousedown'
27238                     })
27239                 }
27240             );
27241         };
27242         // now add all the items...
27243         
27244
27245         if(!this.disable.alignments){
27246             tb.add(
27247                 '-',
27248                 btn('justifyleft'),
27249                 btn('justifycenter'),
27250                 btn('justifyright')
27251             );
27252         };
27253
27254         //if(!Roo.isSafari){
27255             if(!this.disable.links){
27256                 tb.add(
27257                     '-',
27258                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27259                 );
27260             };
27261
27262             if(!this.disable.lists){
27263                 tb.add(
27264                     '-',
27265                     btn('insertorderedlist'),
27266                     btn('insertunorderedlist')
27267                 );
27268             }
27269             if(!this.disable.sourceEdit){
27270                 tb.add(
27271                     '-',
27272                     btn('sourceedit', true, function(btn){
27273                         this.toggleSourceEdit(btn.pressed);
27274                     })
27275                 );
27276             }
27277         //}
27278         
27279         var smenu = { };
27280         // special menu.. - needs to be tidied up..
27281         if (!this.disable.special) {
27282             smenu = {
27283                 text: "&#169;",
27284                 cls: 'x-edit-none',
27285                 
27286                 menu : {
27287                     items : []
27288                 }
27289             };
27290             for (var i =0; i < this.specialChars.length; i++) {
27291                 smenu.menu.items.push({
27292                     
27293                     html: this.specialChars[i],
27294                     handler: function(a,b) {
27295                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27296                         //editor.insertAtCursor(a.html);
27297                         
27298                     },
27299                     tabIndex:-1
27300                 });
27301             }
27302             
27303             
27304             tb.add(smenu);
27305             
27306             
27307         }
27308         
27309         var cmenu = { };
27310         if (!this.disable.cleanStyles) {
27311             cmenu = {
27312                 cls: 'x-btn-icon x-btn-clear',
27313                 
27314                 menu : {
27315                     items : []
27316                 }
27317             };
27318             for (var i =0; i < this.cleanStyles.length; i++) {
27319                 cmenu.menu.items.push({
27320                     actiontype : this.cleanStyles[i],
27321                     html: 'Remove ' + this.cleanStyles[i],
27322                     handler: function(a,b) {
27323 //                        Roo.log(a);
27324 //                        Roo.log(b);
27325                         var c = Roo.get(editorcore.doc.body);
27326                         c.select('[style]').each(function(s) {
27327                             s.dom.style.removeProperty(a.actiontype);
27328                         });
27329                         editorcore.syncValue();
27330                     },
27331                     tabIndex:-1
27332                 });
27333             }
27334              cmenu.menu.items.push({
27335                 actiontype : 'tablewidths',
27336                 html: 'Remove Table Widths',
27337                 handler: function(a,b) {
27338                     editorcore.cleanTableWidths();
27339                     editorcore.syncValue();
27340                 },
27341                 tabIndex:-1
27342             });
27343             cmenu.menu.items.push({
27344                 actiontype : 'word',
27345                 html: 'Remove MS Word Formating',
27346                 handler: function(a,b) {
27347                     editorcore.cleanWord();
27348                     editorcore.syncValue();
27349                 },
27350                 tabIndex:-1
27351             });
27352             
27353             cmenu.menu.items.push({
27354                 actiontype : 'all',
27355                 html: 'Remove All Styles',
27356                 handler: function(a,b) {
27357                     
27358                     var c = Roo.get(editorcore.doc.body);
27359                     c.select('[style]').each(function(s) {
27360                         s.dom.removeAttribute('style');
27361                     });
27362                     editorcore.syncValue();
27363                 },
27364                 tabIndex:-1
27365             });
27366              cmenu.menu.items.push({
27367                 actiontype : 'tidy',
27368                 html: 'Tidy HTML Source',
27369                 handler: function(a,b) {
27370                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27371                     editorcore.syncValue();
27372                 },
27373                 tabIndex:-1
27374             });
27375             
27376             
27377             tb.add(cmenu);
27378         }
27379          
27380         if (!this.disable.specialElements) {
27381             var semenu = {
27382                 text: "Other;",
27383                 cls: 'x-edit-none',
27384                 menu : {
27385                     items : []
27386                 }
27387             };
27388             for (var i =0; i < this.specialElements.length; i++) {
27389                 semenu.menu.items.push(
27390                     Roo.apply({ 
27391                         handler: function(a,b) {
27392                             editor.insertAtCursor(this.ihtml);
27393                         }
27394                     }, this.specialElements[i])
27395                 );
27396                     
27397             }
27398             
27399             tb.add(semenu);
27400             
27401             
27402         }
27403          
27404         
27405         if (this.btns) {
27406             for(var i =0; i< this.btns.length;i++) {
27407                 var b = Roo.factory(this.btns[i],Roo.form);
27408                 b.cls =  'x-edit-none';
27409                 
27410                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27411                     b.cls += ' x-init-enable';
27412                 }
27413                 
27414                 b.scope = editorcore;
27415                 tb.add(b);
27416             }
27417         
27418         }
27419         
27420         
27421         
27422         // disable everything...
27423         
27424         this.tb.items.each(function(item){
27425             
27426            if(
27427                 item.id != editorcore.frameId+ '-sourceedit' && 
27428                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27429             ){
27430                 
27431                 item.disable();
27432             }
27433         });
27434         this.rendered = true;
27435         
27436         // the all the btns;
27437         editor.on('editorevent', this.updateToolbar, this);
27438         // other toolbars need to implement this..
27439         //editor.on('editmodechange', this.updateToolbar, this);
27440     },
27441     
27442     
27443     relayBtnCmd : function(btn) {
27444         this.editorcore.relayCmd(btn.cmd);
27445     },
27446     // private used internally
27447     createLink : function(){
27448         Roo.log("create link?");
27449         var url = prompt(this.createLinkText, this.defaultLinkValue);
27450         if(url && url != 'http:/'+'/'){
27451             this.editorcore.relayCmd('createlink', url);
27452         }
27453     },
27454
27455     
27456     /**
27457      * Protected method that will not generally be called directly. It triggers
27458      * a toolbar update by reading the markup state of the current selection in the editor.
27459      */
27460     updateToolbar: function(){
27461
27462         if(!this.editorcore.activated){
27463             this.editor.onFirstFocus();
27464             return;
27465         }
27466
27467         var btns = this.tb.items.map, 
27468             doc = this.editorcore.doc,
27469             frameId = this.editorcore.frameId;
27470
27471         if(!this.disable.font && !Roo.isSafari){
27472             /*
27473             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27474             if(name != this.fontSelect.dom.value){
27475                 this.fontSelect.dom.value = name;
27476             }
27477             */
27478         }
27479         if(!this.disable.format){
27480             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27481             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27482             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27483         }
27484         if(!this.disable.alignments){
27485             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27486             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27487             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27488         }
27489         if(!Roo.isSafari && !this.disable.lists){
27490             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27491             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27492         }
27493         
27494         var ans = this.editorcore.getAllAncestors();
27495         if (this.formatCombo) {
27496             
27497             
27498             var store = this.formatCombo.store;
27499             this.formatCombo.setValue("");
27500             for (var i =0; i < ans.length;i++) {
27501                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27502                     // select it..
27503                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27504                     break;
27505                 }
27506             }
27507         }
27508         
27509         
27510         
27511         // hides menus... - so this cant be on a menu...
27512         Roo.menu.MenuMgr.hideAll();
27513
27514         //this.editorsyncValue();
27515     },
27516    
27517     
27518     createFontOptions : function(){
27519         var buf = [], fs = this.fontFamilies, ff, lc;
27520         
27521         
27522         
27523         for(var i = 0, len = fs.length; i< len; i++){
27524             ff = fs[i];
27525             lc = ff.toLowerCase();
27526             buf.push(
27527                 '<option value="',lc,'" style="font-family:',ff,';"',
27528                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27529                     ff,
27530                 '</option>'
27531             );
27532         }
27533         return buf.join('');
27534     },
27535     
27536     toggleSourceEdit : function(sourceEditMode){
27537         
27538         Roo.log("toolbar toogle");
27539         if(sourceEditMode === undefined){
27540             sourceEditMode = !this.sourceEditMode;
27541         }
27542         this.sourceEditMode = sourceEditMode === true;
27543         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27544         // just toggle the button?
27545         if(btn.pressed !== this.sourceEditMode){
27546             btn.toggle(this.sourceEditMode);
27547             return;
27548         }
27549         
27550         if(sourceEditMode){
27551             Roo.log("disabling buttons");
27552             this.tb.items.each(function(item){
27553                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27554                     item.disable();
27555                 }
27556             });
27557           
27558         }else{
27559             Roo.log("enabling buttons");
27560             if(this.editorcore.initialized){
27561                 this.tb.items.each(function(item){
27562                     item.enable();
27563                 });
27564             }
27565             
27566         }
27567         Roo.log("calling toggole on editor");
27568         // tell the editor that it's been pressed..
27569         this.editor.toggleSourceEdit(sourceEditMode);
27570        
27571     },
27572      /**
27573      * Object collection of toolbar tooltips for the buttons in the editor. The key
27574      * is the command id associated with that button and the value is a valid QuickTips object.
27575      * For example:
27576 <pre><code>
27577 {
27578     bold : {
27579         title: 'Bold (Ctrl+B)',
27580         text: 'Make the selected text bold.',
27581         cls: 'x-html-editor-tip'
27582     },
27583     italic : {
27584         title: 'Italic (Ctrl+I)',
27585         text: 'Make the selected text italic.',
27586         cls: 'x-html-editor-tip'
27587     },
27588     ...
27589 </code></pre>
27590     * @type Object
27591      */
27592     buttonTips : {
27593         bold : {
27594             title: 'Bold (Ctrl+B)',
27595             text: 'Make the selected text bold.',
27596             cls: 'x-html-editor-tip'
27597         },
27598         italic : {
27599             title: 'Italic (Ctrl+I)',
27600             text: 'Make the selected text italic.',
27601             cls: 'x-html-editor-tip'
27602         },
27603         underline : {
27604             title: 'Underline (Ctrl+U)',
27605             text: 'Underline the selected text.',
27606             cls: 'x-html-editor-tip'
27607         },
27608         increasefontsize : {
27609             title: 'Grow Text',
27610             text: 'Increase the font size.',
27611             cls: 'x-html-editor-tip'
27612         },
27613         decreasefontsize : {
27614             title: 'Shrink Text',
27615             text: 'Decrease the font size.',
27616             cls: 'x-html-editor-tip'
27617         },
27618         backcolor : {
27619             title: 'Text Highlight Color',
27620             text: 'Change the background color of the selected text.',
27621             cls: 'x-html-editor-tip'
27622         },
27623         forecolor : {
27624             title: 'Font Color',
27625             text: 'Change the color of the selected text.',
27626             cls: 'x-html-editor-tip'
27627         },
27628         justifyleft : {
27629             title: 'Align Text Left',
27630             text: 'Align text to the left.',
27631             cls: 'x-html-editor-tip'
27632         },
27633         justifycenter : {
27634             title: 'Center Text',
27635             text: 'Center text in the editor.',
27636             cls: 'x-html-editor-tip'
27637         },
27638         justifyright : {
27639             title: 'Align Text Right',
27640             text: 'Align text to the right.',
27641             cls: 'x-html-editor-tip'
27642         },
27643         insertunorderedlist : {
27644             title: 'Bullet List',
27645             text: 'Start a bulleted list.',
27646             cls: 'x-html-editor-tip'
27647         },
27648         insertorderedlist : {
27649             title: 'Numbered List',
27650             text: 'Start a numbered list.',
27651             cls: 'x-html-editor-tip'
27652         },
27653         createlink : {
27654             title: 'Hyperlink',
27655             text: 'Make the selected text a hyperlink.',
27656             cls: 'x-html-editor-tip'
27657         },
27658         sourceedit : {
27659             title: 'Source Edit',
27660             text: 'Switch to source editing mode.',
27661             cls: 'x-html-editor-tip'
27662         }
27663     },
27664     // private
27665     onDestroy : function(){
27666         if(this.rendered){
27667             
27668             this.tb.items.each(function(item){
27669                 if(item.menu){
27670                     item.menu.removeAll();
27671                     if(item.menu.el){
27672                         item.menu.el.destroy();
27673                     }
27674                 }
27675                 item.destroy();
27676             });
27677              
27678         }
27679     },
27680     onFirstFocus: function() {
27681         this.tb.items.each(function(item){
27682            item.enable();
27683         });
27684     }
27685 });
27686
27687
27688
27689
27690 // <script type="text/javascript">
27691 /*
27692  * Based on
27693  * Ext JS Library 1.1.1
27694  * Copyright(c) 2006-2007, Ext JS, LLC.
27695  *  
27696  
27697  */
27698
27699  
27700 /**
27701  * @class Roo.form.HtmlEditor.ToolbarContext
27702  * Context Toolbar
27703  * 
27704  * Usage:
27705  *
27706  new Roo.form.HtmlEditor({
27707     ....
27708     toolbars : [
27709         { xtype: 'ToolbarStandard', styles : {} }
27710         { xtype: 'ToolbarContext', disable : {} }
27711     ]
27712 })
27713
27714      
27715  * 
27716  * @config : {Object} disable List of elements to disable.. (not done yet.)
27717  * @config : {Object} styles  Map of styles available.
27718  * 
27719  */
27720
27721 Roo.form.HtmlEditor.ToolbarContext = function(config)
27722 {
27723     
27724     Roo.apply(this, config);
27725     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27726     // dont call parent... till later.
27727     this.styles = this.styles || {};
27728 }
27729
27730  
27731
27732 Roo.form.HtmlEditor.ToolbarContext.types = {
27733     'IMG' : {
27734         width : {
27735             title: "Width",
27736             width: 40
27737         },
27738         height:  {
27739             title: "Height",
27740             width: 40
27741         },
27742         align: {
27743             title: "Align",
27744             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27745             width : 80
27746             
27747         },
27748         border: {
27749             title: "Border",
27750             width: 40
27751         },
27752         alt: {
27753             title: "Alt",
27754             width: 120
27755         },
27756         src : {
27757             title: "Src",
27758             width: 220
27759         }
27760         
27761     },
27762     'A' : {
27763         name : {
27764             title: "Name",
27765             width: 50
27766         },
27767         target:  {
27768             title: "Target",
27769             width: 120
27770         },
27771         href:  {
27772             title: "Href",
27773             width: 220
27774         } // border?
27775         
27776     },
27777     'TABLE' : {
27778         rows : {
27779             title: "Rows",
27780             width: 20
27781         },
27782         cols : {
27783             title: "Cols",
27784             width: 20
27785         },
27786         width : {
27787             title: "Width",
27788             width: 40
27789         },
27790         height : {
27791             title: "Height",
27792             width: 40
27793         },
27794         border : {
27795             title: "Border",
27796             width: 20
27797         }
27798     },
27799     'TD' : {
27800         width : {
27801             title: "Width",
27802             width: 40
27803         },
27804         height : {
27805             title: "Height",
27806             width: 40
27807         },   
27808         align: {
27809             title: "Align",
27810             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27811             width: 80
27812         },
27813         valign: {
27814             title: "Valign",
27815             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27816             width: 80
27817         },
27818         colspan: {
27819             title: "Colspan",
27820             width: 20
27821             
27822         },
27823          'font-family'  : {
27824             title : "Font",
27825             style : 'fontFamily',
27826             displayField: 'display',
27827             optname : 'font-family',
27828             width: 140
27829         }
27830     },
27831     'INPUT' : {
27832         name : {
27833             title: "name",
27834             width: 120
27835         },
27836         value : {
27837             title: "Value",
27838             width: 120
27839         },
27840         width : {
27841             title: "Width",
27842             width: 40
27843         }
27844     },
27845     'LABEL' : {
27846         'for' : {
27847             title: "For",
27848             width: 120
27849         }
27850     },
27851     'TEXTAREA' : {
27852           name : {
27853             title: "name",
27854             width: 120
27855         },
27856         rows : {
27857             title: "Rows",
27858             width: 20
27859         },
27860         cols : {
27861             title: "Cols",
27862             width: 20
27863         }
27864     },
27865     'SELECT' : {
27866         name : {
27867             title: "name",
27868             width: 120
27869         },
27870         selectoptions : {
27871             title: "Options",
27872             width: 200
27873         }
27874     },
27875     
27876     // should we really allow this??
27877     // should this just be 
27878     'BODY' : {
27879         title : {
27880             title: "Title",
27881             width: 200,
27882             disabled : true
27883         }
27884     },
27885     'SPAN' : {
27886         'font-family'  : {
27887             title : "Font",
27888             style : 'fontFamily',
27889             displayField: 'display',
27890             optname : 'font-family',
27891             width: 140
27892         }
27893     },
27894     'DIV' : {
27895         'font-family'  : {
27896             title : "Font",
27897             style : 'fontFamily',
27898             displayField: 'display',
27899             optname : 'font-family',
27900             width: 140
27901         }
27902     },
27903      'P' : {
27904         'font-family'  : {
27905             title : "Font",
27906             style : 'fontFamily',
27907             displayField: 'display',
27908             optname : 'font-family',
27909             width: 140
27910         }
27911     },
27912     
27913     '*' : {
27914         // empty..
27915     }
27916
27917 };
27918
27919 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27920 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27921
27922 Roo.form.HtmlEditor.ToolbarContext.options = {
27923         'font-family'  : [ 
27924                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27925                 [ 'Courier New', 'Courier New'],
27926                 [ 'Tahoma', 'Tahoma'],
27927                 [ 'Times New Roman,serif', 'Times'],
27928                 [ 'Verdana','Verdana' ]
27929         ]
27930 };
27931
27932 // fixme - these need to be configurable..
27933  
27934
27935 Roo.form.HtmlEditor.ToolbarContext.types
27936
27937
27938 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27939     
27940     tb: false,
27941     
27942     rendered: false,
27943     
27944     editor : false,
27945     editorcore : false,
27946     /**
27947      * @cfg {Object} disable  List of toolbar elements to disable
27948          
27949      */
27950     disable : false,
27951     /**
27952      * @cfg {Object} styles List of styles 
27953      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27954      *
27955      * These must be defined in the page, so they get rendered correctly..
27956      * .headline { }
27957      * TD.underline { }
27958      * 
27959      */
27960     styles : false,
27961     
27962     options: false,
27963     
27964     toolbars : false,
27965     
27966     init : function(editor)
27967     {
27968         this.editor = editor;
27969         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27970         var editorcore = this.editorcore;
27971         
27972         var fid = editorcore.frameId;
27973         var etb = this;
27974         function btn(id, toggle, handler){
27975             var xid = fid + '-'+ id ;
27976             return {
27977                 id : xid,
27978                 cmd : id,
27979                 cls : 'x-btn-icon x-edit-'+id,
27980                 enableToggle:toggle !== false,
27981                 scope: editorcore, // was editor...
27982                 handler:handler||editorcore.relayBtnCmd,
27983                 clickEvent:'mousedown',
27984                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27985                 tabIndex:-1
27986             };
27987         }
27988         // create a new element.
27989         var wdiv = editor.wrap.createChild({
27990                 tag: 'div'
27991             }, editor.wrap.dom.firstChild.nextSibling, true);
27992         
27993         // can we do this more than once??
27994         
27995          // stop form submits
27996       
27997  
27998         // disable everything...
27999         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28000         this.toolbars = {};
28001            
28002         for (var i in  ty) {
28003           
28004             this.toolbars[i] = this.buildToolbar(ty[i],i);
28005         }
28006         this.tb = this.toolbars.BODY;
28007         this.tb.el.show();
28008         this.buildFooter();
28009         this.footer.show();
28010         editor.on('hide', function( ) { this.footer.hide() }, this);
28011         editor.on('show', function( ) { this.footer.show() }, this);
28012         
28013          
28014         this.rendered = true;
28015         
28016         // the all the btns;
28017         editor.on('editorevent', this.updateToolbar, this);
28018         // other toolbars need to implement this..
28019         //editor.on('editmodechange', this.updateToolbar, this);
28020     },
28021     
28022     
28023     
28024     /**
28025      * Protected method that will not generally be called directly. It triggers
28026      * a toolbar update by reading the markup state of the current selection in the editor.
28027      *
28028      * Note you can force an update by calling on('editorevent', scope, false)
28029      */
28030     updateToolbar: function(editor,ev,sel){
28031
28032         //Roo.log(ev);
28033         // capture mouse up - this is handy for selecting images..
28034         // perhaps should go somewhere else...
28035         if(!this.editorcore.activated){
28036              this.editor.onFirstFocus();
28037             return;
28038         }
28039         
28040         
28041         
28042         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28043         // selectNode - might want to handle IE?
28044         if (ev &&
28045             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28046             ev.target && ev.target.tagName == 'IMG') {
28047             // they have click on an image...
28048             // let's see if we can change the selection...
28049             sel = ev.target;
28050          
28051               var nodeRange = sel.ownerDocument.createRange();
28052             try {
28053                 nodeRange.selectNode(sel);
28054             } catch (e) {
28055                 nodeRange.selectNodeContents(sel);
28056             }
28057             //nodeRange.collapse(true);
28058             var s = this.editorcore.win.getSelection();
28059             s.removeAllRanges();
28060             s.addRange(nodeRange);
28061         }  
28062         
28063       
28064         var updateFooter = sel ? false : true;
28065         
28066         
28067         var ans = this.editorcore.getAllAncestors();
28068         
28069         // pick
28070         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28071         
28072         if (!sel) { 
28073             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28074             sel = sel ? sel : this.editorcore.doc.body;
28075             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28076             
28077         }
28078         // pick a menu that exists..
28079         var tn = sel.tagName.toUpperCase();
28080         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28081         
28082         tn = sel.tagName.toUpperCase();
28083         
28084         var lastSel = this.tb.selectedNode
28085         
28086         this.tb.selectedNode = sel;
28087         
28088         // if current menu does not match..
28089         
28090         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28091                 
28092             this.tb.el.hide();
28093             ///console.log("show: " + tn);
28094             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28095             this.tb.el.show();
28096             // update name
28097             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28098             
28099             
28100             // update attributes
28101             if (this.tb.fields) {
28102                 this.tb.fields.each(function(e) {
28103                     if (e.stylename) {
28104                         e.setValue(sel.style[e.stylename]);
28105                         return;
28106                     } 
28107                    e.setValue(sel.getAttribute(e.attrname));
28108                 });
28109             }
28110             
28111             var hasStyles = false;
28112             for(var i in this.styles) {
28113                 hasStyles = true;
28114                 break;
28115             }
28116             
28117             // update styles
28118             if (hasStyles) { 
28119                 var st = this.tb.fields.item(0);
28120                 
28121                 st.store.removeAll();
28122                
28123                 
28124                 var cn = sel.className.split(/\s+/);
28125                 
28126                 var avs = [];
28127                 if (this.styles['*']) {
28128                     
28129                     Roo.each(this.styles['*'], function(v) {
28130                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28131                     });
28132                 }
28133                 if (this.styles[tn]) { 
28134                     Roo.each(this.styles[tn], function(v) {
28135                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28136                     });
28137                 }
28138                 
28139                 st.store.loadData(avs);
28140                 st.collapse();
28141                 st.setValue(cn);
28142             }
28143             // flag our selected Node.
28144             this.tb.selectedNode = sel;
28145            
28146            
28147             Roo.menu.MenuMgr.hideAll();
28148
28149         }
28150         
28151         if (!updateFooter) {
28152             //this.footDisp.dom.innerHTML = ''; 
28153             return;
28154         }
28155         // update the footer
28156         //
28157         var html = '';
28158         
28159         this.footerEls = ans.reverse();
28160         Roo.each(this.footerEls, function(a,i) {
28161             if (!a) { return; }
28162             html += html.length ? ' &gt; '  :  '';
28163             
28164             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28165             
28166         });
28167        
28168         // 
28169         var sz = this.footDisp.up('td').getSize();
28170         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28171         this.footDisp.dom.style.marginLeft = '5px';
28172         
28173         this.footDisp.dom.style.overflow = 'hidden';
28174         
28175         this.footDisp.dom.innerHTML = html;
28176             
28177         //this.editorsyncValue();
28178     },
28179      
28180     
28181    
28182        
28183     // private
28184     onDestroy : function(){
28185         if(this.rendered){
28186             
28187             this.tb.items.each(function(item){
28188                 if(item.menu){
28189                     item.menu.removeAll();
28190                     if(item.menu.el){
28191                         item.menu.el.destroy();
28192                     }
28193                 }
28194                 item.destroy();
28195             });
28196              
28197         }
28198     },
28199     onFirstFocus: function() {
28200         // need to do this for all the toolbars..
28201         this.tb.items.each(function(item){
28202            item.enable();
28203         });
28204     },
28205     buildToolbar: function(tlist, nm)
28206     {
28207         var editor = this.editor;
28208         var editorcore = this.editorcore;
28209          // create a new element.
28210         var wdiv = editor.wrap.createChild({
28211                 tag: 'div'
28212             }, editor.wrap.dom.firstChild.nextSibling, true);
28213         
28214        
28215         var tb = new Roo.Toolbar(wdiv);
28216         // add the name..
28217         
28218         tb.add(nm+ ":&nbsp;");
28219         
28220         var styles = [];
28221         for(var i in this.styles) {
28222             styles.push(i);
28223         }
28224         
28225         // styles...
28226         if (styles && styles.length) {
28227             
28228             // this needs a multi-select checkbox...
28229             tb.addField( new Roo.form.ComboBox({
28230                 store: new Roo.data.SimpleStore({
28231                     id : 'val',
28232                     fields: ['val', 'selected'],
28233                     data : [] 
28234                 }),
28235                 name : '-roo-edit-className',
28236                 attrname : 'className',
28237                 displayField: 'val',
28238                 typeAhead: false,
28239                 mode: 'local',
28240                 editable : false,
28241                 triggerAction: 'all',
28242                 emptyText:'Select Style',
28243                 selectOnFocus:true,
28244                 width: 130,
28245                 listeners : {
28246                     'select': function(c, r, i) {
28247                         // initial support only for on class per el..
28248                         tb.selectedNode.className =  r ? r.get('val') : '';
28249                         editorcore.syncValue();
28250                     }
28251                 }
28252     
28253             }));
28254         }
28255         
28256         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28257         var tbops = tbc.options;
28258         
28259         for (var i in tlist) {
28260             
28261             var item = tlist[i];
28262             tb.add(item.title + ":&nbsp;");
28263             
28264             
28265             //optname == used so you can configure the options available..
28266             var opts = item.opts ? item.opts : false;
28267             if (item.optname) {
28268                 opts = tbops[item.optname];
28269            
28270             }
28271             
28272             if (opts) {
28273                 // opts == pulldown..
28274                 tb.addField( new Roo.form.ComboBox({
28275                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28276                         id : 'val',
28277                         fields: ['val', 'display'],
28278                         data : opts  
28279                     }),
28280                     name : '-roo-edit-' + i,
28281                     attrname : i,
28282                     stylename : item.style ? item.style : false,
28283                     displayField: item.displayField ? item.displayField : 'val',
28284                     valueField :  'val',
28285                     typeAhead: false,
28286                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28287                     editable : false,
28288                     triggerAction: 'all',
28289                     emptyText:'Select',
28290                     selectOnFocus:true,
28291                     width: item.width ? item.width  : 130,
28292                     listeners : {
28293                         'select': function(c, r, i) {
28294                             if (c.stylename) {
28295                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28296                                 return;
28297                             }
28298                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28299                         }
28300                     }
28301
28302                 }));
28303                 continue;
28304                     
28305                  
28306                 
28307                 tb.addField( new Roo.form.TextField({
28308                     name: i,
28309                     width: 100,
28310                     //allowBlank:false,
28311                     value: ''
28312                 }));
28313                 continue;
28314             }
28315             tb.addField( new Roo.form.TextField({
28316                 name: '-roo-edit-' + i,
28317                 attrname : i,
28318                 
28319                 width: item.width,
28320                 //allowBlank:true,
28321                 value: '',
28322                 listeners: {
28323                     'change' : function(f, nv, ov) {
28324                         tb.selectedNode.setAttribute(f.attrname, nv);
28325                     }
28326                 }
28327             }));
28328              
28329         }
28330         
28331         var _this = this;
28332         
28333         if(nm == 'BODY'){
28334             tb.addSeparator();
28335         
28336             tb.addButton( {
28337                 text: 'Stylesheets',
28338
28339                 listeners : {
28340                     click : function ()
28341                     {
28342                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28343                     }
28344                 }
28345             });
28346         }
28347         
28348         tb.addFill();
28349         tb.addButton( {
28350             text: 'Remove Tag',
28351     
28352             listeners : {
28353                 click : function ()
28354                 {
28355                     // remove
28356                     // undo does not work.
28357                      
28358                     var sn = tb.selectedNode;
28359                     
28360                     var pn = sn.parentNode;
28361                     
28362                     var stn =  sn.childNodes[0];
28363                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28364                     while (sn.childNodes.length) {
28365                         var node = sn.childNodes[0];
28366                         sn.removeChild(node);
28367                         //Roo.log(node);
28368                         pn.insertBefore(node, sn);
28369                         
28370                     }
28371                     pn.removeChild(sn);
28372                     var range = editorcore.createRange();
28373         
28374                     range.setStart(stn,0);
28375                     range.setEnd(en,0); //????
28376                     //range.selectNode(sel);
28377                     
28378                     
28379                     var selection = editorcore.getSelection();
28380                     selection.removeAllRanges();
28381                     selection.addRange(range);
28382                     
28383                     
28384                     
28385                     //_this.updateToolbar(null, null, pn);
28386                     _this.updateToolbar(null, null, null);
28387                     _this.footDisp.dom.innerHTML = ''; 
28388                 }
28389             }
28390             
28391                     
28392                 
28393             
28394         });
28395         
28396         
28397         tb.el.on('click', function(e){
28398             e.preventDefault(); // what does this do?
28399         });
28400         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28401         tb.el.hide();
28402         tb.name = nm;
28403         // dont need to disable them... as they will get hidden
28404         return tb;
28405          
28406         
28407     },
28408     buildFooter : function()
28409     {
28410         
28411         var fel = this.editor.wrap.createChild();
28412         this.footer = new Roo.Toolbar(fel);
28413         // toolbar has scrolly on left / right?
28414         var footDisp= new Roo.Toolbar.Fill();
28415         var _t = this;
28416         this.footer.add(
28417             {
28418                 text : '&lt;',
28419                 xtype: 'Button',
28420                 handler : function() {
28421                     _t.footDisp.scrollTo('left',0,true)
28422                 }
28423             }
28424         );
28425         this.footer.add( footDisp );
28426         this.footer.add( 
28427             {
28428                 text : '&gt;',
28429                 xtype: 'Button',
28430                 handler : function() {
28431                     // no animation..
28432                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28433                 }
28434             }
28435         );
28436         var fel = Roo.get(footDisp.el);
28437         fel.addClass('x-editor-context');
28438         this.footDispWrap = fel; 
28439         this.footDispWrap.overflow  = 'hidden';
28440         
28441         this.footDisp = fel.createChild();
28442         this.footDispWrap.on('click', this.onContextClick, this)
28443         
28444         
28445     },
28446     onContextClick : function (ev,dom)
28447     {
28448         ev.preventDefault();
28449         var  cn = dom.className;
28450         //Roo.log(cn);
28451         if (!cn.match(/x-ed-loc-/)) {
28452             return;
28453         }
28454         var n = cn.split('-').pop();
28455         var ans = this.footerEls;
28456         var sel = ans[n];
28457         
28458          // pick
28459         var range = this.editorcore.createRange();
28460         
28461         range.selectNodeContents(sel);
28462         //range.selectNode(sel);
28463         
28464         
28465         var selection = this.editorcore.getSelection();
28466         selection.removeAllRanges();
28467         selection.addRange(range);
28468         
28469         
28470         
28471         this.updateToolbar(null, null, sel);
28472         
28473         
28474     }
28475     
28476     
28477     
28478     
28479     
28480 });
28481
28482
28483
28484
28485
28486 /*
28487  * Based on:
28488  * Ext JS Library 1.1.1
28489  * Copyright(c) 2006-2007, Ext JS, LLC.
28490  *
28491  * Originally Released Under LGPL - original licence link has changed is not relivant.
28492  *
28493  * Fork - LGPL
28494  * <script type="text/javascript">
28495  */
28496  
28497 /**
28498  * @class Roo.form.BasicForm
28499  * @extends Roo.util.Observable
28500  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28501  * @constructor
28502  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28503  * @param {Object} config Configuration options
28504  */
28505 Roo.form.BasicForm = function(el, config){
28506     this.allItems = [];
28507     this.childForms = [];
28508     Roo.apply(this, config);
28509     /*
28510      * The Roo.form.Field items in this form.
28511      * @type MixedCollection
28512      */
28513      
28514      
28515     this.items = new Roo.util.MixedCollection(false, function(o){
28516         return o.id || (o.id = Roo.id());
28517     });
28518     this.addEvents({
28519         /**
28520          * @event beforeaction
28521          * Fires before any action is performed. Return false to cancel the action.
28522          * @param {Form} this
28523          * @param {Action} action The action to be performed
28524          */
28525         beforeaction: true,
28526         /**
28527          * @event actionfailed
28528          * Fires when an action fails.
28529          * @param {Form} this
28530          * @param {Action} action The action that failed
28531          */
28532         actionfailed : true,
28533         /**
28534          * @event actioncomplete
28535          * Fires when an action is completed.
28536          * @param {Form} this
28537          * @param {Action} action The action that completed
28538          */
28539         actioncomplete : true
28540     });
28541     if(el){
28542         this.initEl(el);
28543     }
28544     Roo.form.BasicForm.superclass.constructor.call(this);
28545 };
28546
28547 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28548     /**
28549      * @cfg {String} method
28550      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28551      */
28552     /**
28553      * @cfg {DataReader} reader
28554      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28555      * This is optional as there is built-in support for processing JSON.
28556      */
28557     /**
28558      * @cfg {DataReader} errorReader
28559      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28560      * This is completely optional as there is built-in support for processing JSON.
28561      */
28562     /**
28563      * @cfg {String} url
28564      * The URL to use for form actions if one isn't supplied in the action options.
28565      */
28566     /**
28567      * @cfg {Boolean} fileUpload
28568      * Set to true if this form is a file upload.
28569      */
28570      
28571     /**
28572      * @cfg {Object} baseParams
28573      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28574      */
28575      /**
28576      
28577     /**
28578      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28579      */
28580     timeout: 30,
28581
28582     // private
28583     activeAction : null,
28584
28585     /**
28586      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28587      * or setValues() data instead of when the form was first created.
28588      */
28589     trackResetOnLoad : false,
28590     
28591     
28592     /**
28593      * childForms - used for multi-tab forms
28594      * @type {Array}
28595      */
28596     childForms : false,
28597     
28598     /**
28599      * allItems - full list of fields.
28600      * @type {Array}
28601      */
28602     allItems : false,
28603     
28604     /**
28605      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28606      * element by passing it or its id or mask the form itself by passing in true.
28607      * @type Mixed
28608      */
28609     waitMsgTarget : false,
28610
28611     // private
28612     initEl : function(el){
28613         this.el = Roo.get(el);
28614         this.id = this.el.id || Roo.id();
28615         this.el.on('submit', this.onSubmit, this);
28616         this.el.addClass('x-form');
28617     },
28618
28619     // private
28620     onSubmit : function(e){
28621         e.stopEvent();
28622     },
28623
28624     /**
28625      * Returns true if client-side validation on the form is successful.
28626      * @return Boolean
28627      */
28628     isValid : function(){
28629         var valid = true;
28630         this.items.each(function(f){
28631            if(!f.validate()){
28632                valid = false;
28633            }
28634         });
28635         return valid;
28636     },
28637
28638     /**
28639      * Returns true if any fields in this form have changed since their original load.
28640      * @return Boolean
28641      */
28642     isDirty : function(){
28643         var dirty = false;
28644         this.items.each(function(f){
28645            if(f.isDirty()){
28646                dirty = true;
28647                return false;
28648            }
28649         });
28650         return dirty;
28651     },
28652
28653     /**
28654      * Performs a predefined action (submit or load) or custom actions you define on this form.
28655      * @param {String} actionName The name of the action type
28656      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28657      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28658      * accept other config options):
28659      * <pre>
28660 Property          Type             Description
28661 ----------------  ---------------  ----------------------------------------------------------------------------------
28662 url               String           The url for the action (defaults to the form's url)
28663 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28664 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28665 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28666                                    validate the form on the client (defaults to false)
28667      * </pre>
28668      * @return {BasicForm} this
28669      */
28670     doAction : function(action, options){
28671         if(typeof action == 'string'){
28672             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28673         }
28674         if(this.fireEvent('beforeaction', this, action) !== false){
28675             this.beforeAction(action);
28676             action.run.defer(100, action);
28677         }
28678         return this;
28679     },
28680
28681     /**
28682      * Shortcut to do a submit action.
28683      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28684      * @return {BasicForm} this
28685      */
28686     submit : function(options){
28687         this.doAction('submit', options);
28688         return this;
28689     },
28690
28691     /**
28692      * Shortcut to do a load action.
28693      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28694      * @return {BasicForm} this
28695      */
28696     load : function(options){
28697         this.doAction('load', options);
28698         return this;
28699     },
28700
28701     /**
28702      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28703      * @param {Record} record The record to edit
28704      * @return {BasicForm} this
28705      */
28706     updateRecord : function(record){
28707         record.beginEdit();
28708         var fs = record.fields;
28709         fs.each(function(f){
28710             var field = this.findField(f.name);
28711             if(field){
28712                 record.set(f.name, field.getValue());
28713             }
28714         }, this);
28715         record.endEdit();
28716         return this;
28717     },
28718
28719     /**
28720      * Loads an Roo.data.Record into this form.
28721      * @param {Record} record The record to load
28722      * @return {BasicForm} this
28723      */
28724     loadRecord : function(record){
28725         this.setValues(record.data);
28726         return this;
28727     },
28728
28729     // private
28730     beforeAction : function(action){
28731         var o = action.options;
28732         
28733        
28734         if(this.waitMsgTarget === true){
28735             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28736         }else if(this.waitMsgTarget){
28737             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28738             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28739         }else {
28740             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28741         }
28742          
28743     },
28744
28745     // private
28746     afterAction : function(action, success){
28747         this.activeAction = null;
28748         var o = action.options;
28749         
28750         if(this.waitMsgTarget === true){
28751             this.el.unmask();
28752         }else if(this.waitMsgTarget){
28753             this.waitMsgTarget.unmask();
28754         }else{
28755             Roo.MessageBox.updateProgress(1);
28756             Roo.MessageBox.hide();
28757         }
28758          
28759         if(success){
28760             if(o.reset){
28761                 this.reset();
28762             }
28763             Roo.callback(o.success, o.scope, [this, action]);
28764             this.fireEvent('actioncomplete', this, action);
28765             
28766         }else{
28767             
28768             // failure condition..
28769             // we have a scenario where updates need confirming.
28770             // eg. if a locking scenario exists..
28771             // we look for { errors : { needs_confirm : true }} in the response.
28772             if (
28773                 (typeof(action.result) != 'undefined')  &&
28774                 (typeof(action.result.errors) != 'undefined')  &&
28775                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28776            ){
28777                 var _t = this;
28778                 Roo.MessageBox.confirm(
28779                     "Change requires confirmation",
28780                     action.result.errorMsg,
28781                     function(r) {
28782                         if (r != 'yes') {
28783                             return;
28784                         }
28785                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28786                     }
28787                     
28788                 );
28789                 
28790                 
28791                 
28792                 return;
28793             }
28794             
28795             Roo.callback(o.failure, o.scope, [this, action]);
28796             // show an error message if no failed handler is set..
28797             if (!this.hasListener('actionfailed')) {
28798                 Roo.MessageBox.alert("Error",
28799                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28800                         action.result.errorMsg :
28801                         "Saving Failed, please check your entries or try again"
28802                 );
28803             }
28804             
28805             this.fireEvent('actionfailed', this, action);
28806         }
28807         
28808     },
28809
28810     /**
28811      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28812      * @param {String} id The value to search for
28813      * @return Field
28814      */
28815     findField : function(id){
28816         var field = this.items.get(id);
28817         if(!field){
28818             this.items.each(function(f){
28819                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28820                     field = f;
28821                     return false;
28822                 }
28823             });
28824         }
28825         return field || null;
28826     },
28827
28828     /**
28829      * Add a secondary form to this one, 
28830      * Used to provide tabbed forms. One form is primary, with hidden values 
28831      * which mirror the elements from the other forms.
28832      * 
28833      * @param {Roo.form.Form} form to add.
28834      * 
28835      */
28836     addForm : function(form)
28837     {
28838        
28839         if (this.childForms.indexOf(form) > -1) {
28840             // already added..
28841             return;
28842         }
28843         this.childForms.push(form);
28844         var n = '';
28845         Roo.each(form.allItems, function (fe) {
28846             
28847             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28848             if (this.findField(n)) { // already added..
28849                 return;
28850             }
28851             var add = new Roo.form.Hidden({
28852                 name : n
28853             });
28854             add.render(this.el);
28855             
28856             this.add( add );
28857         }, this);
28858         
28859     },
28860     /**
28861      * Mark fields in this form invalid in bulk.
28862      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28863      * @return {BasicForm} this
28864      */
28865     markInvalid : function(errors){
28866         if(errors instanceof Array){
28867             for(var i = 0, len = errors.length; i < len; i++){
28868                 var fieldError = errors[i];
28869                 var f = this.findField(fieldError.id);
28870                 if(f){
28871                     f.markInvalid(fieldError.msg);
28872                 }
28873             }
28874         }else{
28875             var field, id;
28876             for(id in errors){
28877                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28878                     field.markInvalid(errors[id]);
28879                 }
28880             }
28881         }
28882         Roo.each(this.childForms || [], function (f) {
28883             f.markInvalid(errors);
28884         });
28885         
28886         return this;
28887     },
28888
28889     /**
28890      * Set values for fields in this form in bulk.
28891      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28892      * @return {BasicForm} this
28893      */
28894     setValues : function(values){
28895         if(values instanceof Array){ // array of objects
28896             for(var i = 0, len = values.length; i < len; i++){
28897                 var v = values[i];
28898                 var f = this.findField(v.id);
28899                 if(f){
28900                     f.setValue(v.value);
28901                     if(this.trackResetOnLoad){
28902                         f.originalValue = f.getValue();
28903                     }
28904                 }
28905             }
28906         }else{ // object hash
28907             var field, id;
28908             for(id in values){
28909                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28910                     
28911                     if (field.setFromData && 
28912                         field.valueField && 
28913                         field.displayField &&
28914                         // combos' with local stores can 
28915                         // be queried via setValue()
28916                         // to set their value..
28917                         (field.store && !field.store.isLocal)
28918                         ) {
28919                         // it's a combo
28920                         var sd = { };
28921                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28922                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28923                         field.setFromData(sd);
28924                         
28925                     } else {
28926                         field.setValue(values[id]);
28927                     }
28928                     
28929                     
28930                     if(this.trackResetOnLoad){
28931                         field.originalValue = field.getValue();
28932                     }
28933                 }
28934             }
28935         }
28936          
28937         Roo.each(this.childForms || [], function (f) {
28938             f.setValues(values);
28939         });
28940                 
28941         return this;
28942     },
28943
28944     /**
28945      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28946      * they are returned as an array.
28947      * @param {Boolean} asString
28948      * @return {Object}
28949      */
28950     getValues : function(asString){
28951         if (this.childForms) {
28952             // copy values from the child forms
28953             Roo.each(this.childForms, function (f) {
28954                 this.setValues(f.getValues());
28955             }, this);
28956         }
28957         
28958         
28959         
28960         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28961         if(asString === true){
28962             return fs;
28963         }
28964         return Roo.urlDecode(fs);
28965     },
28966     
28967     /**
28968      * Returns the fields in this form as an object with key/value pairs. 
28969      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28970      * @return {Object}
28971      */
28972     getFieldValues : function(with_hidden)
28973     {
28974         if (this.childForms) {
28975             // copy values from the child forms
28976             // should this call getFieldValues - probably not as we do not currently copy
28977             // hidden fields when we generate..
28978             Roo.each(this.childForms, function (f) {
28979                 this.setValues(f.getValues());
28980             }, this);
28981         }
28982         
28983         var ret = {};
28984         this.items.each(function(f){
28985             if (!f.getName()) {
28986                 return;
28987             }
28988             var v = f.getValue();
28989             if (f.inputType =='radio') {
28990                 if (typeof(ret[f.getName()]) == 'undefined') {
28991                     ret[f.getName()] = ''; // empty..
28992                 }
28993                 
28994                 if (!f.el.dom.checked) {
28995                     return;
28996                     
28997                 }
28998                 v = f.el.dom.value;
28999                 
29000             }
29001             
29002             // not sure if this supported any more..
29003             if ((typeof(v) == 'object') && f.getRawValue) {
29004                 v = f.getRawValue() ; // dates..
29005             }
29006             // combo boxes where name != hiddenName...
29007             if (f.name != f.getName()) {
29008                 ret[f.name] = f.getRawValue();
29009             }
29010             ret[f.getName()] = v;
29011         });
29012         
29013         return ret;
29014     },
29015
29016     /**
29017      * Clears all invalid messages in this form.
29018      * @return {BasicForm} this
29019      */
29020     clearInvalid : function(){
29021         this.items.each(function(f){
29022            f.clearInvalid();
29023         });
29024         
29025         Roo.each(this.childForms || [], function (f) {
29026             f.clearInvalid();
29027         });
29028         
29029         
29030         return this;
29031     },
29032
29033     /**
29034      * Resets this form.
29035      * @return {BasicForm} this
29036      */
29037     reset : function(){
29038         this.items.each(function(f){
29039             f.reset();
29040         });
29041         
29042         Roo.each(this.childForms || [], function (f) {
29043             f.reset();
29044         });
29045        
29046         
29047         return this;
29048     },
29049
29050     /**
29051      * Add Roo.form components to this form.
29052      * @param {Field} field1
29053      * @param {Field} field2 (optional)
29054      * @param {Field} etc (optional)
29055      * @return {BasicForm} this
29056      */
29057     add : function(){
29058         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29059         return this;
29060     },
29061
29062
29063     /**
29064      * Removes a field from the items collection (does NOT remove its markup).
29065      * @param {Field} field
29066      * @return {BasicForm} this
29067      */
29068     remove : function(field){
29069         this.items.remove(field);
29070         return this;
29071     },
29072
29073     /**
29074      * Looks at the fields in this form, checks them for an id attribute,
29075      * and calls applyTo on the existing dom element with that id.
29076      * @return {BasicForm} this
29077      */
29078     render : function(){
29079         this.items.each(function(f){
29080             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29081                 f.applyTo(f.id);
29082             }
29083         });
29084         return this;
29085     },
29086
29087     /**
29088      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29089      * @param {Object} values
29090      * @return {BasicForm} this
29091      */
29092     applyToFields : function(o){
29093         this.items.each(function(f){
29094            Roo.apply(f, o);
29095         });
29096         return this;
29097     },
29098
29099     /**
29100      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29101      * @param {Object} values
29102      * @return {BasicForm} this
29103      */
29104     applyIfToFields : function(o){
29105         this.items.each(function(f){
29106            Roo.applyIf(f, o);
29107         });
29108         return this;
29109     }
29110 });
29111
29112 // back compat
29113 Roo.BasicForm = Roo.form.BasicForm;/*
29114  * Based on:
29115  * Ext JS Library 1.1.1
29116  * Copyright(c) 2006-2007, Ext JS, LLC.
29117  *
29118  * Originally Released Under LGPL - original licence link has changed is not relivant.
29119  *
29120  * Fork - LGPL
29121  * <script type="text/javascript">
29122  */
29123
29124 /**
29125  * @class Roo.form.Form
29126  * @extends Roo.form.BasicForm
29127  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29128  * @constructor
29129  * @param {Object} config Configuration options
29130  */
29131 Roo.form.Form = function(config){
29132     var xitems =  [];
29133     if (config.items) {
29134         xitems = config.items;
29135         delete config.items;
29136     }
29137    
29138     
29139     Roo.form.Form.superclass.constructor.call(this, null, config);
29140     this.url = this.url || this.action;
29141     if(!this.root){
29142         this.root = new Roo.form.Layout(Roo.applyIf({
29143             id: Roo.id()
29144         }, config));
29145     }
29146     this.active = this.root;
29147     /**
29148      * Array of all the buttons that have been added to this form via {@link addButton}
29149      * @type Array
29150      */
29151     this.buttons = [];
29152     this.allItems = [];
29153     this.addEvents({
29154         /**
29155          * @event clientvalidation
29156          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29157          * @param {Form} this
29158          * @param {Boolean} valid true if the form has passed client-side validation
29159          */
29160         clientvalidation: true,
29161         /**
29162          * @event rendered
29163          * Fires when the form is rendered
29164          * @param {Roo.form.Form} form
29165          */
29166         rendered : true
29167     });
29168     
29169     if (this.progressUrl) {
29170             // push a hidden field onto the list of fields..
29171             this.addxtype( {
29172                     xns: Roo.form, 
29173                     xtype : 'Hidden', 
29174                     name : 'UPLOAD_IDENTIFIER' 
29175             });
29176         }
29177         
29178     
29179     Roo.each(xitems, this.addxtype, this);
29180     
29181     
29182     
29183 };
29184
29185 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29186     /**
29187      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29188      */
29189     /**
29190      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29191      */
29192     /**
29193      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29194      */
29195     buttonAlign:'center',
29196
29197     /**
29198      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29199      */
29200     minButtonWidth:75,
29201
29202     /**
29203      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29204      * This property cascades to child containers if not set.
29205      */
29206     labelAlign:'left',
29207
29208     /**
29209      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29210      * fires a looping event with that state. This is required to bind buttons to the valid
29211      * state using the config value formBind:true on the button.
29212      */
29213     monitorValid : false,
29214
29215     /**
29216      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29217      */
29218     monitorPoll : 200,
29219     
29220     /**
29221      * @cfg {String} progressUrl - Url to return progress data 
29222      */
29223     
29224     progressUrl : false,
29225   
29226     /**
29227      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29228      * fields are added and the column is closed. If no fields are passed the column remains open
29229      * until end() is called.
29230      * @param {Object} config The config to pass to the column
29231      * @param {Field} field1 (optional)
29232      * @param {Field} field2 (optional)
29233      * @param {Field} etc (optional)
29234      * @return Column The column container object
29235      */
29236     column : function(c){
29237         var col = new Roo.form.Column(c);
29238         this.start(col);
29239         if(arguments.length > 1){ // duplicate code required because of Opera
29240             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29241             this.end();
29242         }
29243         return col;
29244     },
29245
29246     /**
29247      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29248      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29249      * until end() is called.
29250      * @param {Object} config The config to pass to the fieldset
29251      * @param {Field} field1 (optional)
29252      * @param {Field} field2 (optional)
29253      * @param {Field} etc (optional)
29254      * @return FieldSet The fieldset container object
29255      */
29256     fieldset : function(c){
29257         var fs = new Roo.form.FieldSet(c);
29258         this.start(fs);
29259         if(arguments.length > 1){ // duplicate code required because of Opera
29260             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29261             this.end();
29262         }
29263         return fs;
29264     },
29265
29266     /**
29267      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29268      * fields are added and the container is closed. If no fields are passed the container remains open
29269      * until end() is called.
29270      * @param {Object} config The config to pass to the Layout
29271      * @param {Field} field1 (optional)
29272      * @param {Field} field2 (optional)
29273      * @param {Field} etc (optional)
29274      * @return Layout The container object
29275      */
29276     container : function(c){
29277         var l = new Roo.form.Layout(c);
29278         this.start(l);
29279         if(arguments.length > 1){ // duplicate code required because of Opera
29280             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29281             this.end();
29282         }
29283         return l;
29284     },
29285
29286     /**
29287      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29288      * @param {Object} container A Roo.form.Layout or subclass of Layout
29289      * @return {Form} this
29290      */
29291     start : function(c){
29292         // cascade label info
29293         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29294         this.active.stack.push(c);
29295         c.ownerCt = this.active;
29296         this.active = c;
29297         return this;
29298     },
29299
29300     /**
29301      * Closes the current open container
29302      * @return {Form} this
29303      */
29304     end : function(){
29305         if(this.active == this.root){
29306             return this;
29307         }
29308         this.active = this.active.ownerCt;
29309         return this;
29310     },
29311
29312     /**
29313      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29314      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29315      * as the label of the field.
29316      * @param {Field} field1
29317      * @param {Field} field2 (optional)
29318      * @param {Field} etc. (optional)
29319      * @return {Form} this
29320      */
29321     add : function(){
29322         this.active.stack.push.apply(this.active.stack, arguments);
29323         this.allItems.push.apply(this.allItems,arguments);
29324         var r = [];
29325         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29326             if(a[i].isFormField){
29327                 r.push(a[i]);
29328             }
29329         }
29330         if(r.length > 0){
29331             Roo.form.Form.superclass.add.apply(this, r);
29332         }
29333         return this;
29334     },
29335     
29336
29337     
29338     
29339     
29340      /**
29341      * Find any element that has been added to a form, using it's ID or name
29342      * This can include framesets, columns etc. along with regular fields..
29343      * @param {String} id - id or name to find.
29344      
29345      * @return {Element} e - or false if nothing found.
29346      */
29347     findbyId : function(id)
29348     {
29349         var ret = false;
29350         if (!id) {
29351             return ret;
29352         }
29353         Roo.each(this.allItems, function(f){
29354             if (f.id == id || f.name == id ){
29355                 ret = f;
29356                 return false;
29357             }
29358         });
29359         return ret;
29360     },
29361
29362     
29363     
29364     /**
29365      * Render this form into the passed container. This should only be called once!
29366      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29367      * @return {Form} this
29368      */
29369     render : function(ct)
29370     {
29371         
29372         
29373         
29374         ct = Roo.get(ct);
29375         var o = this.autoCreate || {
29376             tag: 'form',
29377             method : this.method || 'POST',
29378             id : this.id || Roo.id()
29379         };
29380         this.initEl(ct.createChild(o));
29381
29382         this.root.render(this.el);
29383         
29384        
29385              
29386         this.items.each(function(f){
29387             f.render('x-form-el-'+f.id);
29388         });
29389
29390         if(this.buttons.length > 0){
29391             // tables are required to maintain order and for correct IE layout
29392             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29393                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29394                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29395             }}, null, true);
29396             var tr = tb.getElementsByTagName('tr')[0];
29397             for(var i = 0, len = this.buttons.length; i < len; i++) {
29398                 var b = this.buttons[i];
29399                 var td = document.createElement('td');
29400                 td.className = 'x-form-btn-td';
29401                 b.render(tr.appendChild(td));
29402             }
29403         }
29404         if(this.monitorValid){ // initialize after render
29405             this.startMonitoring();
29406         }
29407         this.fireEvent('rendered', this);
29408         return this;
29409     },
29410
29411     /**
29412      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29413      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29414      * object or a valid Roo.DomHelper element config
29415      * @param {Function} handler The function called when the button is clicked
29416      * @param {Object} scope (optional) The scope of the handler function
29417      * @return {Roo.Button}
29418      */
29419     addButton : function(config, handler, scope){
29420         var bc = {
29421             handler: handler,
29422             scope: scope,
29423             minWidth: this.minButtonWidth,
29424             hideParent:true
29425         };
29426         if(typeof config == "string"){
29427             bc.text = config;
29428         }else{
29429             Roo.apply(bc, config);
29430         }
29431         var btn = new Roo.Button(null, bc);
29432         this.buttons.push(btn);
29433         return btn;
29434     },
29435
29436      /**
29437      * Adds a series of form elements (using the xtype property as the factory method.
29438      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29439      * @param {Object} config 
29440      */
29441     
29442     addxtype : function()
29443     {
29444         var ar = Array.prototype.slice.call(arguments, 0);
29445         var ret = false;
29446         for(var i = 0; i < ar.length; i++) {
29447             if (!ar[i]) {
29448                 continue; // skip -- if this happends something invalid got sent, we 
29449                 // should ignore it, as basically that interface element will not show up
29450                 // and that should be pretty obvious!!
29451             }
29452             
29453             if (Roo.form[ar[i].xtype]) {
29454                 ar[i].form = this;
29455                 var fe = Roo.factory(ar[i], Roo.form);
29456                 if (!ret) {
29457                     ret = fe;
29458                 }
29459                 fe.form = this;
29460                 if (fe.store) {
29461                     fe.store.form = this;
29462                 }
29463                 if (fe.isLayout) {  
29464                          
29465                     this.start(fe);
29466                     this.allItems.push(fe);
29467                     if (fe.items && fe.addxtype) {
29468                         fe.addxtype.apply(fe, fe.items);
29469                         delete fe.items;
29470                     }
29471                      this.end();
29472                     continue;
29473                 }
29474                 
29475                 
29476                  
29477                 this.add(fe);
29478               //  console.log('adding ' + ar[i].xtype);
29479             }
29480             if (ar[i].xtype == 'Button') {  
29481                 //console.log('adding button');
29482                 //console.log(ar[i]);
29483                 this.addButton(ar[i]);
29484                 this.allItems.push(fe);
29485                 continue;
29486             }
29487             
29488             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29489                 alert('end is not supported on xtype any more, use items');
29490             //    this.end();
29491             //    //console.log('adding end');
29492             }
29493             
29494         }
29495         return ret;
29496     },
29497     
29498     /**
29499      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29500      * option "monitorValid"
29501      */
29502     startMonitoring : function(){
29503         if(!this.bound){
29504             this.bound = true;
29505             Roo.TaskMgr.start({
29506                 run : this.bindHandler,
29507                 interval : this.monitorPoll || 200,
29508                 scope: this
29509             });
29510         }
29511     },
29512
29513     /**
29514      * Stops monitoring of the valid state of this form
29515      */
29516     stopMonitoring : function(){
29517         this.bound = false;
29518     },
29519
29520     // private
29521     bindHandler : function(){
29522         if(!this.bound){
29523             return false; // stops binding
29524         }
29525         var valid = true;
29526         this.items.each(function(f){
29527             if(!f.isValid(true)){
29528                 valid = false;
29529                 return false;
29530             }
29531         });
29532         for(var i = 0, len = this.buttons.length; i < len; i++){
29533             var btn = this.buttons[i];
29534             if(btn.formBind === true && btn.disabled === valid){
29535                 btn.setDisabled(!valid);
29536             }
29537         }
29538         this.fireEvent('clientvalidation', this, valid);
29539     }
29540     
29541     
29542     
29543     
29544     
29545     
29546     
29547     
29548 });
29549
29550
29551 // back compat
29552 Roo.Form = Roo.form.Form;
29553 /*
29554  * Based on:
29555  * Ext JS Library 1.1.1
29556  * Copyright(c) 2006-2007, Ext JS, LLC.
29557  *
29558  * Originally Released Under LGPL - original licence link has changed is not relivant.
29559  *
29560  * Fork - LGPL
29561  * <script type="text/javascript">
29562  */
29563
29564 // as we use this in bootstrap.
29565 Roo.namespace('Roo.form');
29566  /**
29567  * @class Roo.form.Action
29568  * Internal Class used to handle form actions
29569  * @constructor
29570  * @param {Roo.form.BasicForm} el The form element or its id
29571  * @param {Object} config Configuration options
29572  */
29573
29574  
29575  
29576 // define the action interface
29577 Roo.form.Action = function(form, options){
29578     this.form = form;
29579     this.options = options || {};
29580 };
29581 /**
29582  * Client Validation Failed
29583  * @const 
29584  */
29585 Roo.form.Action.CLIENT_INVALID = 'client';
29586 /**
29587  * Server Validation Failed
29588  * @const 
29589  */
29590 Roo.form.Action.SERVER_INVALID = 'server';
29591  /**
29592  * Connect to Server Failed
29593  * @const 
29594  */
29595 Roo.form.Action.CONNECT_FAILURE = 'connect';
29596 /**
29597  * Reading Data from Server Failed
29598  * @const 
29599  */
29600 Roo.form.Action.LOAD_FAILURE = 'load';
29601
29602 Roo.form.Action.prototype = {
29603     type : 'default',
29604     failureType : undefined,
29605     response : undefined,
29606     result : undefined,
29607
29608     // interface method
29609     run : function(options){
29610
29611     },
29612
29613     // interface method
29614     success : function(response){
29615
29616     },
29617
29618     // interface method
29619     handleResponse : function(response){
29620
29621     },
29622
29623     // default connection failure
29624     failure : function(response){
29625         
29626         this.response = response;
29627         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29628         this.form.afterAction(this, false);
29629     },
29630
29631     processResponse : function(response){
29632         this.response = response;
29633         if(!response.responseText){
29634             return true;
29635         }
29636         this.result = this.handleResponse(response);
29637         return this.result;
29638     },
29639
29640     // utility functions used internally
29641     getUrl : function(appendParams){
29642         var url = this.options.url || this.form.url || this.form.el.dom.action;
29643         if(appendParams){
29644             var p = this.getParams();
29645             if(p){
29646                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29647             }
29648         }
29649         return url;
29650     },
29651
29652     getMethod : function(){
29653         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29654     },
29655
29656     getParams : function(){
29657         var bp = this.form.baseParams;
29658         var p = this.options.params;
29659         if(p){
29660             if(typeof p == "object"){
29661                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29662             }else if(typeof p == 'string' && bp){
29663                 p += '&' + Roo.urlEncode(bp);
29664             }
29665         }else if(bp){
29666             p = Roo.urlEncode(bp);
29667         }
29668         return p;
29669     },
29670
29671     createCallback : function(){
29672         return {
29673             success: this.success,
29674             failure: this.failure,
29675             scope: this,
29676             timeout: (this.form.timeout*1000),
29677             upload: this.form.fileUpload ? this.success : undefined
29678         };
29679     }
29680 };
29681
29682 Roo.form.Action.Submit = function(form, options){
29683     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29684 };
29685
29686 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29687     type : 'submit',
29688
29689     haveProgress : false,
29690     uploadComplete : false,
29691     
29692     // uploadProgress indicator.
29693     uploadProgress : function()
29694     {
29695         if (!this.form.progressUrl) {
29696             return;
29697         }
29698         
29699         if (!this.haveProgress) {
29700             Roo.MessageBox.progress("Uploading", "Uploading");
29701         }
29702         if (this.uploadComplete) {
29703            Roo.MessageBox.hide();
29704            return;
29705         }
29706         
29707         this.haveProgress = true;
29708    
29709         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29710         
29711         var c = new Roo.data.Connection();
29712         c.request({
29713             url : this.form.progressUrl,
29714             params: {
29715                 id : uid
29716             },
29717             method: 'GET',
29718             success : function(req){
29719                //console.log(data);
29720                 var rdata = false;
29721                 var edata;
29722                 try  {
29723                    rdata = Roo.decode(req.responseText)
29724                 } catch (e) {
29725                     Roo.log("Invalid data from server..");
29726                     Roo.log(edata);
29727                     return;
29728                 }
29729                 if (!rdata || !rdata.success) {
29730                     Roo.log(rdata);
29731                     Roo.MessageBox.alert(Roo.encode(rdata));
29732                     return;
29733                 }
29734                 var data = rdata.data;
29735                 
29736                 if (this.uploadComplete) {
29737                    Roo.MessageBox.hide();
29738                    return;
29739                 }
29740                    
29741                 if (data){
29742                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29743                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29744                     );
29745                 }
29746                 this.uploadProgress.defer(2000,this);
29747             },
29748        
29749             failure: function(data) {
29750                 Roo.log('progress url failed ');
29751                 Roo.log(data);
29752             },
29753             scope : this
29754         });
29755            
29756     },
29757     
29758     
29759     run : function()
29760     {
29761         // run get Values on the form, so it syncs any secondary forms.
29762         this.form.getValues();
29763         
29764         var o = this.options;
29765         var method = this.getMethod();
29766         var isPost = method == 'POST';
29767         if(o.clientValidation === false || this.form.isValid()){
29768             
29769             if (this.form.progressUrl) {
29770                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29771                     (new Date() * 1) + '' + Math.random());
29772                     
29773             } 
29774             
29775             
29776             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29777                 form:this.form.el.dom,
29778                 url:this.getUrl(!isPost),
29779                 method: method,
29780                 params:isPost ? this.getParams() : null,
29781                 isUpload: this.form.fileUpload
29782             }));
29783             
29784             this.uploadProgress();
29785
29786         }else if (o.clientValidation !== false){ // client validation failed
29787             this.failureType = Roo.form.Action.CLIENT_INVALID;
29788             this.form.afterAction(this, false);
29789         }
29790     },
29791
29792     success : function(response)
29793     {
29794         this.uploadComplete= true;
29795         if (this.haveProgress) {
29796             Roo.MessageBox.hide();
29797         }
29798         
29799         
29800         var result = this.processResponse(response);
29801         if(result === true || result.success){
29802             this.form.afterAction(this, true);
29803             return;
29804         }
29805         if(result.errors){
29806             this.form.markInvalid(result.errors);
29807             this.failureType = Roo.form.Action.SERVER_INVALID;
29808         }
29809         this.form.afterAction(this, false);
29810     },
29811     failure : function(response)
29812     {
29813         this.uploadComplete= true;
29814         if (this.haveProgress) {
29815             Roo.MessageBox.hide();
29816         }
29817         
29818         this.response = response;
29819         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29820         this.form.afterAction(this, false);
29821     },
29822     
29823     handleResponse : function(response){
29824         if(this.form.errorReader){
29825             var rs = this.form.errorReader.read(response);
29826             var errors = [];
29827             if(rs.records){
29828                 for(var i = 0, len = rs.records.length; i < len; i++) {
29829                     var r = rs.records[i];
29830                     errors[i] = r.data;
29831                 }
29832             }
29833             if(errors.length < 1){
29834                 errors = null;
29835             }
29836             return {
29837                 success : rs.success,
29838                 errors : errors
29839             };
29840         }
29841         var ret = false;
29842         try {
29843             ret = Roo.decode(response.responseText);
29844         } catch (e) {
29845             ret = {
29846                 success: false,
29847                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29848                 errors : []
29849             };
29850         }
29851         return ret;
29852         
29853     }
29854 });
29855
29856
29857 Roo.form.Action.Load = function(form, options){
29858     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29859     this.reader = this.form.reader;
29860 };
29861
29862 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29863     type : 'load',
29864
29865     run : function(){
29866         
29867         Roo.Ajax.request(Roo.apply(
29868                 this.createCallback(), {
29869                     method:this.getMethod(),
29870                     url:this.getUrl(false),
29871                     params:this.getParams()
29872         }));
29873     },
29874
29875     success : function(response){
29876         
29877         var result = this.processResponse(response);
29878         if(result === true || !result.success || !result.data){
29879             this.failureType = Roo.form.Action.LOAD_FAILURE;
29880             this.form.afterAction(this, false);
29881             return;
29882         }
29883         this.form.clearInvalid();
29884         this.form.setValues(result.data);
29885         this.form.afterAction(this, true);
29886     },
29887
29888     handleResponse : function(response){
29889         if(this.form.reader){
29890             var rs = this.form.reader.read(response);
29891             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29892             return {
29893                 success : rs.success,
29894                 data : data
29895             };
29896         }
29897         return Roo.decode(response.responseText);
29898     }
29899 });
29900
29901 Roo.form.Action.ACTION_TYPES = {
29902     'load' : Roo.form.Action.Load,
29903     'submit' : Roo.form.Action.Submit
29904 };/*
29905  * Based on:
29906  * Ext JS Library 1.1.1
29907  * Copyright(c) 2006-2007, Ext JS, LLC.
29908  *
29909  * Originally Released Under LGPL - original licence link has changed is not relivant.
29910  *
29911  * Fork - LGPL
29912  * <script type="text/javascript">
29913  */
29914  
29915 /**
29916  * @class Roo.form.Layout
29917  * @extends Roo.Component
29918  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29919  * @constructor
29920  * @param {Object} config Configuration options
29921  */
29922 Roo.form.Layout = function(config){
29923     var xitems = [];
29924     if (config.items) {
29925         xitems = config.items;
29926         delete config.items;
29927     }
29928     Roo.form.Layout.superclass.constructor.call(this, config);
29929     this.stack = [];
29930     Roo.each(xitems, this.addxtype, this);
29931      
29932 };
29933
29934 Roo.extend(Roo.form.Layout, Roo.Component, {
29935     /**
29936      * @cfg {String/Object} autoCreate
29937      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29938      */
29939     /**
29940      * @cfg {String/Object/Function} style
29941      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29942      * a function which returns such a specification.
29943      */
29944     /**
29945      * @cfg {String} labelAlign
29946      * Valid values are "left," "top" and "right" (defaults to "left")
29947      */
29948     /**
29949      * @cfg {Number} labelWidth
29950      * Fixed width in pixels of all field labels (defaults to undefined)
29951      */
29952     /**
29953      * @cfg {Boolean} clear
29954      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29955      */
29956     clear : true,
29957     /**
29958      * @cfg {String} labelSeparator
29959      * The separator to use after field labels (defaults to ':')
29960      */
29961     labelSeparator : ':',
29962     /**
29963      * @cfg {Boolean} hideLabels
29964      * True to suppress the display of field labels in this layout (defaults to false)
29965      */
29966     hideLabels : false,
29967
29968     // private
29969     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29970     
29971     isLayout : true,
29972     
29973     // private
29974     onRender : function(ct, position){
29975         if(this.el){ // from markup
29976             this.el = Roo.get(this.el);
29977         }else {  // generate
29978             var cfg = this.getAutoCreate();
29979             this.el = ct.createChild(cfg, position);
29980         }
29981         if(this.style){
29982             this.el.applyStyles(this.style);
29983         }
29984         if(this.labelAlign){
29985             this.el.addClass('x-form-label-'+this.labelAlign);
29986         }
29987         if(this.hideLabels){
29988             this.labelStyle = "display:none";
29989             this.elementStyle = "padding-left:0;";
29990         }else{
29991             if(typeof this.labelWidth == 'number'){
29992                 this.labelStyle = "width:"+this.labelWidth+"px;";
29993                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29994             }
29995             if(this.labelAlign == 'top'){
29996                 this.labelStyle = "width:auto;";
29997                 this.elementStyle = "padding-left:0;";
29998             }
29999         }
30000         var stack = this.stack;
30001         var slen = stack.length;
30002         if(slen > 0){
30003             if(!this.fieldTpl){
30004                 var t = new Roo.Template(
30005                     '<div class="x-form-item {5}">',
30006                         '<label for="{0}" style="{2}">{1}{4}</label>',
30007                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30008                         '</div>',
30009                     '</div><div class="x-form-clear-left"></div>'
30010                 );
30011                 t.disableFormats = true;
30012                 t.compile();
30013                 Roo.form.Layout.prototype.fieldTpl = t;
30014             }
30015             for(var i = 0; i < slen; i++) {
30016                 if(stack[i].isFormField){
30017                     this.renderField(stack[i]);
30018                 }else{
30019                     this.renderComponent(stack[i]);
30020                 }
30021             }
30022         }
30023         if(this.clear){
30024             this.el.createChild({cls:'x-form-clear'});
30025         }
30026     },
30027
30028     // private
30029     renderField : function(f){
30030         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30031                f.id, //0
30032                f.fieldLabel, //1
30033                f.labelStyle||this.labelStyle||'', //2
30034                this.elementStyle||'', //3
30035                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30036                f.itemCls||this.itemCls||''  //5
30037        ], true).getPrevSibling());
30038     },
30039
30040     // private
30041     renderComponent : function(c){
30042         c.render(c.isLayout ? this.el : this.el.createChild());    
30043     },
30044     /**
30045      * Adds a object form elements (using the xtype property as the factory method.)
30046      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30047      * @param {Object} config 
30048      */
30049     addxtype : function(o)
30050     {
30051         // create the lement.
30052         o.form = this.form;
30053         var fe = Roo.factory(o, Roo.form);
30054         this.form.allItems.push(fe);
30055         this.stack.push(fe);
30056         
30057         if (fe.isFormField) {
30058             this.form.items.add(fe);
30059         }
30060          
30061         return fe;
30062     }
30063 });
30064
30065 /**
30066  * @class Roo.form.Column
30067  * @extends Roo.form.Layout
30068  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30069  * @constructor
30070  * @param {Object} config Configuration options
30071  */
30072 Roo.form.Column = function(config){
30073     Roo.form.Column.superclass.constructor.call(this, config);
30074 };
30075
30076 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30077     /**
30078      * @cfg {Number/String} width
30079      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30080      */
30081     /**
30082      * @cfg {String/Object} autoCreate
30083      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30084      */
30085
30086     // private
30087     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30088
30089     // private
30090     onRender : function(ct, position){
30091         Roo.form.Column.superclass.onRender.call(this, ct, position);
30092         if(this.width){
30093             this.el.setWidth(this.width);
30094         }
30095     }
30096 });
30097
30098
30099 /**
30100  * @class Roo.form.Row
30101  * @extends Roo.form.Layout
30102  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30103  * @constructor
30104  * @param {Object} config Configuration options
30105  */
30106
30107  
30108 Roo.form.Row = function(config){
30109     Roo.form.Row.superclass.constructor.call(this, config);
30110 };
30111  
30112 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30113       /**
30114      * @cfg {Number/String} width
30115      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30116      */
30117     /**
30118      * @cfg {Number/String} height
30119      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30120      */
30121     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30122     
30123     padWidth : 20,
30124     // private
30125     onRender : function(ct, position){
30126         //console.log('row render');
30127         if(!this.rowTpl){
30128             var t = new Roo.Template(
30129                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30130                     '<label for="{0}" style="{2}">{1}{4}</label>',
30131                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30132                     '</div>',
30133                 '</div>'
30134             );
30135             t.disableFormats = true;
30136             t.compile();
30137             Roo.form.Layout.prototype.rowTpl = t;
30138         }
30139         this.fieldTpl = this.rowTpl;
30140         
30141         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30142         var labelWidth = 100;
30143         
30144         if ((this.labelAlign != 'top')) {
30145             if (typeof this.labelWidth == 'number') {
30146                 labelWidth = this.labelWidth
30147             }
30148             this.padWidth =  20 + labelWidth;
30149             
30150         }
30151         
30152         Roo.form.Column.superclass.onRender.call(this, ct, position);
30153         if(this.width){
30154             this.el.setWidth(this.width);
30155         }
30156         if(this.height){
30157             this.el.setHeight(this.height);
30158         }
30159     },
30160     
30161     // private
30162     renderField : function(f){
30163         f.fieldEl = this.fieldTpl.append(this.el, [
30164                f.id, f.fieldLabel,
30165                f.labelStyle||this.labelStyle||'',
30166                this.elementStyle||'',
30167                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30168                f.itemCls||this.itemCls||'',
30169                f.width ? f.width + this.padWidth : 160 + this.padWidth
30170        ],true);
30171     }
30172 });
30173  
30174
30175 /**
30176  * @class Roo.form.FieldSet
30177  * @extends Roo.form.Layout
30178  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30179  * @constructor
30180  * @param {Object} config Configuration options
30181  */
30182 Roo.form.FieldSet = function(config){
30183     Roo.form.FieldSet.superclass.constructor.call(this, config);
30184 };
30185
30186 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30187     /**
30188      * @cfg {String} legend
30189      * The text to display as the legend for the FieldSet (defaults to '')
30190      */
30191     /**
30192      * @cfg {String/Object} autoCreate
30193      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30194      */
30195
30196     // private
30197     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30198
30199     // private
30200     onRender : function(ct, position){
30201         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30202         if(this.legend){
30203             this.setLegend(this.legend);
30204         }
30205     },
30206
30207     // private
30208     setLegend : function(text){
30209         if(this.rendered){
30210             this.el.child('legend').update(text);
30211         }
30212     }
30213 });/*
30214  * Based on:
30215  * Ext JS Library 1.1.1
30216  * Copyright(c) 2006-2007, Ext JS, LLC.
30217  *
30218  * Originally Released Under LGPL - original licence link has changed is not relivant.
30219  *
30220  * Fork - LGPL
30221  * <script type="text/javascript">
30222  */
30223 /**
30224  * @class Roo.form.VTypes
30225  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30226  * @singleton
30227  */
30228 Roo.form.VTypes = function(){
30229     // closure these in so they are only created once.
30230     var alpha = /^[a-zA-Z_]+$/;
30231     var alphanum = /^[a-zA-Z0-9_]+$/;
30232     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30233     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30234
30235     // All these messages and functions are configurable
30236     return {
30237         /**
30238          * The function used to validate email addresses
30239          * @param {String} value The email address
30240          */
30241         'email' : function(v){
30242             return email.test(v);
30243         },
30244         /**
30245          * The error text to display when the email validation function returns false
30246          * @type String
30247          */
30248         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30249         /**
30250          * The keystroke filter mask to be applied on email input
30251          * @type RegExp
30252          */
30253         'emailMask' : /[a-z0-9_\.\-@]/i,
30254
30255         /**
30256          * The function used to validate URLs
30257          * @param {String} value The URL
30258          */
30259         'url' : function(v){
30260             return url.test(v);
30261         },
30262         /**
30263          * The error text to display when the url validation function returns false
30264          * @type String
30265          */
30266         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30267         
30268         /**
30269          * The function used to validate alpha values
30270          * @param {String} value The value
30271          */
30272         'alpha' : function(v){
30273             return alpha.test(v);
30274         },
30275         /**
30276          * The error text to display when the alpha validation function returns false
30277          * @type String
30278          */
30279         'alphaText' : 'This field should only contain letters and _',
30280         /**
30281          * The keystroke filter mask to be applied on alpha input
30282          * @type RegExp
30283          */
30284         'alphaMask' : /[a-z_]/i,
30285
30286         /**
30287          * The function used to validate alphanumeric values
30288          * @param {String} value The value
30289          */
30290         'alphanum' : function(v){
30291             return alphanum.test(v);
30292         },
30293         /**
30294          * The error text to display when the alphanumeric validation function returns false
30295          * @type String
30296          */
30297         'alphanumText' : 'This field should only contain letters, numbers and _',
30298         /**
30299          * The keystroke filter mask to be applied on alphanumeric input
30300          * @type RegExp
30301          */
30302         'alphanumMask' : /[a-z0-9_]/i
30303     };
30304 }();//<script type="text/javascript">
30305
30306 /**
30307  * @class Roo.form.FCKeditor
30308  * @extends Roo.form.TextArea
30309  * Wrapper around the FCKEditor http://www.fckeditor.net
30310  * @constructor
30311  * Creates a new FCKeditor
30312  * @param {Object} config Configuration options
30313  */
30314 Roo.form.FCKeditor = function(config){
30315     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30316     this.addEvents({
30317          /**
30318          * @event editorinit
30319          * Fired when the editor is initialized - you can add extra handlers here..
30320          * @param {FCKeditor} this
30321          * @param {Object} the FCK object.
30322          */
30323         editorinit : true
30324     });
30325     
30326     
30327 };
30328 Roo.form.FCKeditor.editors = { };
30329 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30330 {
30331     //defaultAutoCreate : {
30332     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30333     //},
30334     // private
30335     /**
30336      * @cfg {Object} fck options - see fck manual for details.
30337      */
30338     fckconfig : false,
30339     
30340     /**
30341      * @cfg {Object} fck toolbar set (Basic or Default)
30342      */
30343     toolbarSet : 'Basic',
30344     /**
30345      * @cfg {Object} fck BasePath
30346      */ 
30347     basePath : '/fckeditor/',
30348     
30349     
30350     frame : false,
30351     
30352     value : '',
30353     
30354    
30355     onRender : function(ct, position)
30356     {
30357         if(!this.el){
30358             this.defaultAutoCreate = {
30359                 tag: "textarea",
30360                 style:"width:300px;height:60px;",
30361                 autocomplete: "new-password"
30362             };
30363         }
30364         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30365         /*
30366         if(this.grow){
30367             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30368             if(this.preventScrollbars){
30369                 this.el.setStyle("overflow", "hidden");
30370             }
30371             this.el.setHeight(this.growMin);
30372         }
30373         */
30374         //console.log('onrender' + this.getId() );
30375         Roo.form.FCKeditor.editors[this.getId()] = this;
30376          
30377
30378         this.replaceTextarea() ;
30379         
30380     },
30381     
30382     getEditor : function() {
30383         return this.fckEditor;
30384     },
30385     /**
30386      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30387      * @param {Mixed} value The value to set
30388      */
30389     
30390     
30391     setValue : function(value)
30392     {
30393         //console.log('setValue: ' + value);
30394         
30395         if(typeof(value) == 'undefined') { // not sure why this is happending...
30396             return;
30397         }
30398         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30399         
30400         //if(!this.el || !this.getEditor()) {
30401         //    this.value = value;
30402             //this.setValue.defer(100,this,[value]);    
30403         //    return;
30404         //} 
30405         
30406         if(!this.getEditor()) {
30407             return;
30408         }
30409         
30410         this.getEditor().SetData(value);
30411         
30412         //
30413
30414     },
30415
30416     /**
30417      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30418      * @return {Mixed} value The field value
30419      */
30420     getValue : function()
30421     {
30422         
30423         if (this.frame && this.frame.dom.style.display == 'none') {
30424             return Roo.form.FCKeditor.superclass.getValue.call(this);
30425         }
30426         
30427         if(!this.el || !this.getEditor()) {
30428            
30429            // this.getValue.defer(100,this); 
30430             return this.value;
30431         }
30432        
30433         
30434         var value=this.getEditor().GetData();
30435         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30436         return Roo.form.FCKeditor.superclass.getValue.call(this);
30437         
30438
30439     },
30440
30441     /**
30442      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30443      * @return {Mixed} value The field value
30444      */
30445     getRawValue : function()
30446     {
30447         if (this.frame && this.frame.dom.style.display == 'none') {
30448             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30449         }
30450         
30451         if(!this.el || !this.getEditor()) {
30452             //this.getRawValue.defer(100,this); 
30453             return this.value;
30454             return;
30455         }
30456         
30457         
30458         
30459         var value=this.getEditor().GetData();
30460         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30461         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30462          
30463     },
30464     
30465     setSize : function(w,h) {
30466         
30467         
30468         
30469         //if (this.frame && this.frame.dom.style.display == 'none') {
30470         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30471         //    return;
30472         //}
30473         //if(!this.el || !this.getEditor()) {
30474         //    this.setSize.defer(100,this, [w,h]); 
30475         //    return;
30476         //}
30477         
30478         
30479         
30480         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30481         
30482         this.frame.dom.setAttribute('width', w);
30483         this.frame.dom.setAttribute('height', h);
30484         this.frame.setSize(w,h);
30485         
30486     },
30487     
30488     toggleSourceEdit : function(value) {
30489         
30490       
30491          
30492         this.el.dom.style.display = value ? '' : 'none';
30493         this.frame.dom.style.display = value ?  'none' : '';
30494         
30495     },
30496     
30497     
30498     focus: function(tag)
30499     {
30500         if (this.frame.dom.style.display == 'none') {
30501             return Roo.form.FCKeditor.superclass.focus.call(this);
30502         }
30503         if(!this.el || !this.getEditor()) {
30504             this.focus.defer(100,this, [tag]); 
30505             return;
30506         }
30507         
30508         
30509         
30510         
30511         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30512         this.getEditor().Focus();
30513         if (tgs.length) {
30514             if (!this.getEditor().Selection.GetSelection()) {
30515                 this.focus.defer(100,this, [tag]); 
30516                 return;
30517             }
30518             
30519             
30520             var r = this.getEditor().EditorDocument.createRange();
30521             r.setStart(tgs[0],0);
30522             r.setEnd(tgs[0],0);
30523             this.getEditor().Selection.GetSelection().removeAllRanges();
30524             this.getEditor().Selection.GetSelection().addRange(r);
30525             this.getEditor().Focus();
30526         }
30527         
30528     },
30529     
30530     
30531     
30532     replaceTextarea : function()
30533     {
30534         if ( document.getElementById( this.getId() + '___Frame' ) )
30535             return ;
30536         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30537         //{
30538             // We must check the elements firstly using the Id and then the name.
30539         var oTextarea = document.getElementById( this.getId() );
30540         
30541         var colElementsByName = document.getElementsByName( this.getId() ) ;
30542          
30543         oTextarea.style.display = 'none' ;
30544
30545         if ( oTextarea.tabIndex ) {            
30546             this.TabIndex = oTextarea.tabIndex ;
30547         }
30548         
30549         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30550         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30551         this.frame = Roo.get(this.getId() + '___Frame')
30552     },
30553     
30554     _getConfigHtml : function()
30555     {
30556         var sConfig = '' ;
30557
30558         for ( var o in this.fckconfig ) {
30559             sConfig += sConfig.length > 0  ? '&amp;' : '';
30560             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30561         }
30562
30563         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30564     },
30565     
30566     
30567     _getIFrameHtml : function()
30568     {
30569         var sFile = 'fckeditor.html' ;
30570         /* no idea what this is about..
30571         try
30572         {
30573             if ( (/fcksource=true/i).test( window.top.location.search ) )
30574                 sFile = 'fckeditor.original.html' ;
30575         }
30576         catch (e) { 
30577         */
30578
30579         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30580         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30581         
30582         
30583         var html = '<iframe id="' + this.getId() +
30584             '___Frame" src="' + sLink +
30585             '" width="' + this.width +
30586             '" height="' + this.height + '"' +
30587             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30588             ' frameborder="0" scrolling="no"></iframe>' ;
30589
30590         return html ;
30591     },
30592     
30593     _insertHtmlBefore : function( html, element )
30594     {
30595         if ( element.insertAdjacentHTML )       {
30596             // IE
30597             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30598         } else { // Gecko
30599             var oRange = document.createRange() ;
30600             oRange.setStartBefore( element ) ;
30601             var oFragment = oRange.createContextualFragment( html );
30602             element.parentNode.insertBefore( oFragment, element ) ;
30603         }
30604     }
30605     
30606     
30607   
30608     
30609     
30610     
30611     
30612
30613 });
30614
30615 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30616
30617 function FCKeditor_OnComplete(editorInstance){
30618     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30619     f.fckEditor = editorInstance;
30620     //console.log("loaded");
30621     f.fireEvent('editorinit', f, editorInstance);
30622
30623   
30624
30625  
30626
30627
30628
30629
30630
30631
30632
30633
30634
30635
30636
30637
30638
30639
30640
30641 //<script type="text/javascript">
30642 /**
30643  * @class Roo.form.GridField
30644  * @extends Roo.form.Field
30645  * Embed a grid (or editable grid into a form)
30646  * STATUS ALPHA
30647  * 
30648  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30649  * it needs 
30650  * xgrid.store = Roo.data.Store
30651  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30652  * xgrid.store.reader = Roo.data.JsonReader 
30653  * 
30654  * 
30655  * @constructor
30656  * Creates a new GridField
30657  * @param {Object} config Configuration options
30658  */
30659 Roo.form.GridField = function(config){
30660     Roo.form.GridField.superclass.constructor.call(this, config);
30661      
30662 };
30663
30664 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30665     /**
30666      * @cfg {Number} width  - used to restrict width of grid..
30667      */
30668     width : 100,
30669     /**
30670      * @cfg {Number} height - used to restrict height of grid..
30671      */
30672     height : 50,
30673      /**
30674      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30675          * 
30676          *}
30677      */
30678     xgrid : false, 
30679     /**
30680      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30681      * {tag: "input", type: "checkbox", autocomplete: "off"})
30682      */
30683    // defaultAutoCreate : { tag: 'div' },
30684     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30685     /**
30686      * @cfg {String} addTitle Text to include for adding a title.
30687      */
30688     addTitle : false,
30689     //
30690     onResize : function(){
30691         Roo.form.Field.superclass.onResize.apply(this, arguments);
30692     },
30693
30694     initEvents : function(){
30695         // Roo.form.Checkbox.superclass.initEvents.call(this);
30696         // has no events...
30697        
30698     },
30699
30700
30701     getResizeEl : function(){
30702         return this.wrap;
30703     },
30704
30705     getPositionEl : function(){
30706         return this.wrap;
30707     },
30708
30709     // private
30710     onRender : function(ct, position){
30711         
30712         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30713         var style = this.style;
30714         delete this.style;
30715         
30716         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30717         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30718         this.viewEl = this.wrap.createChild({ tag: 'div' });
30719         if (style) {
30720             this.viewEl.applyStyles(style);
30721         }
30722         if (this.width) {
30723             this.viewEl.setWidth(this.width);
30724         }
30725         if (this.height) {
30726             this.viewEl.setHeight(this.height);
30727         }
30728         //if(this.inputValue !== undefined){
30729         //this.setValue(this.value);
30730         
30731         
30732         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30733         
30734         
30735         this.grid.render();
30736         this.grid.getDataSource().on('remove', this.refreshValue, this);
30737         this.grid.getDataSource().on('update', this.refreshValue, this);
30738         this.grid.on('afteredit', this.refreshValue, this);
30739  
30740     },
30741      
30742     
30743     /**
30744      * Sets the value of the item. 
30745      * @param {String} either an object  or a string..
30746      */
30747     setValue : function(v){
30748         //this.value = v;
30749         v = v || []; // empty set..
30750         // this does not seem smart - it really only affects memoryproxy grids..
30751         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30752             var ds = this.grid.getDataSource();
30753             // assumes a json reader..
30754             var data = {}
30755             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30756             ds.loadData( data);
30757         }
30758         // clear selection so it does not get stale.
30759         if (this.grid.sm) { 
30760             this.grid.sm.clearSelections();
30761         }
30762         
30763         Roo.form.GridField.superclass.setValue.call(this, v);
30764         this.refreshValue();
30765         // should load data in the grid really....
30766     },
30767     
30768     // private
30769     refreshValue: function() {
30770          var val = [];
30771         this.grid.getDataSource().each(function(r) {
30772             val.push(r.data);
30773         });
30774         this.el.dom.value = Roo.encode(val);
30775     }
30776     
30777      
30778     
30779     
30780 });/*
30781  * Based on:
30782  * Ext JS Library 1.1.1
30783  * Copyright(c) 2006-2007, Ext JS, LLC.
30784  *
30785  * Originally Released Under LGPL - original licence link has changed is not relivant.
30786  *
30787  * Fork - LGPL
30788  * <script type="text/javascript">
30789  */
30790 /**
30791  * @class Roo.form.DisplayField
30792  * @extends Roo.form.Field
30793  * A generic Field to display non-editable data.
30794  * @constructor
30795  * Creates a new Display Field item.
30796  * @param {Object} config Configuration options
30797  */
30798 Roo.form.DisplayField = function(config){
30799     Roo.form.DisplayField.superclass.constructor.call(this, config);
30800     
30801 };
30802
30803 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30804     inputType:      'hidden',
30805     allowBlank:     true,
30806     readOnly:         true,
30807     
30808  
30809     /**
30810      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30811      */
30812     focusClass : undefined,
30813     /**
30814      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30815      */
30816     fieldClass: 'x-form-field',
30817     
30818      /**
30819      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30820      */
30821     valueRenderer: undefined,
30822     
30823     width: 100,
30824     /**
30825      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30826      * {tag: "input", type: "checkbox", autocomplete: "off"})
30827      */
30828      
30829  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30830
30831     onResize : function(){
30832         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30833         
30834     },
30835
30836     initEvents : function(){
30837         // Roo.form.Checkbox.superclass.initEvents.call(this);
30838         // has no events...
30839        
30840     },
30841
30842
30843     getResizeEl : function(){
30844         return this.wrap;
30845     },
30846
30847     getPositionEl : function(){
30848         return this.wrap;
30849     },
30850
30851     // private
30852     onRender : function(ct, position){
30853         
30854         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30855         //if(this.inputValue !== undefined){
30856         this.wrap = this.el.wrap();
30857         
30858         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30859         
30860         if (this.bodyStyle) {
30861             this.viewEl.applyStyles(this.bodyStyle);
30862         }
30863         //this.viewEl.setStyle('padding', '2px');
30864         
30865         this.setValue(this.value);
30866         
30867     },
30868 /*
30869     // private
30870     initValue : Roo.emptyFn,
30871
30872   */
30873
30874         // private
30875     onClick : function(){
30876         
30877     },
30878
30879     /**
30880      * Sets the checked state of the checkbox.
30881      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30882      */
30883     setValue : function(v){
30884         this.value = v;
30885         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30886         // this might be called before we have a dom element..
30887         if (!this.viewEl) {
30888             return;
30889         }
30890         this.viewEl.dom.innerHTML = html;
30891         Roo.form.DisplayField.superclass.setValue.call(this, v);
30892
30893     }
30894 });/*
30895  * 
30896  * Licence- LGPL
30897  * 
30898  */
30899
30900 /**
30901  * @class Roo.form.DayPicker
30902  * @extends Roo.form.Field
30903  * A Day picker show [M] [T] [W] ....
30904  * @constructor
30905  * Creates a new Day Picker
30906  * @param {Object} config Configuration options
30907  */
30908 Roo.form.DayPicker= function(config){
30909     Roo.form.DayPicker.superclass.constructor.call(this, config);
30910      
30911 };
30912
30913 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30914     /**
30915      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30916      */
30917     focusClass : undefined,
30918     /**
30919      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30920      */
30921     fieldClass: "x-form-field",
30922    
30923     /**
30924      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30925      * {tag: "input", type: "checkbox", autocomplete: "off"})
30926      */
30927     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30928     
30929    
30930     actionMode : 'viewEl', 
30931     //
30932     // private
30933  
30934     inputType : 'hidden',
30935     
30936      
30937     inputElement: false, // real input element?
30938     basedOn: false, // ????
30939     
30940     isFormField: true, // not sure where this is needed!!!!
30941
30942     onResize : function(){
30943         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30944         if(!this.boxLabel){
30945             this.el.alignTo(this.wrap, 'c-c');
30946         }
30947     },
30948
30949     initEvents : function(){
30950         Roo.form.Checkbox.superclass.initEvents.call(this);
30951         this.el.on("click", this.onClick,  this);
30952         this.el.on("change", this.onClick,  this);
30953     },
30954
30955
30956     getResizeEl : function(){
30957         return this.wrap;
30958     },
30959
30960     getPositionEl : function(){
30961         return this.wrap;
30962     },
30963
30964     
30965     // private
30966     onRender : function(ct, position){
30967         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30968        
30969         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30970         
30971         var r1 = '<table><tr>';
30972         var r2 = '<tr class="x-form-daypick-icons">';
30973         for (var i=0; i < 7; i++) {
30974             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30975             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30976         }
30977         
30978         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30979         viewEl.select('img').on('click', this.onClick, this);
30980         this.viewEl = viewEl;   
30981         
30982         
30983         // this will not work on Chrome!!!
30984         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30985         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30986         
30987         
30988           
30989
30990     },
30991
30992     // private
30993     initValue : Roo.emptyFn,
30994
30995     /**
30996      * Returns the checked state of the checkbox.
30997      * @return {Boolean} True if checked, else false
30998      */
30999     getValue : function(){
31000         return this.el.dom.value;
31001         
31002     },
31003
31004         // private
31005     onClick : function(e){ 
31006         //this.setChecked(!this.checked);
31007         Roo.get(e.target).toggleClass('x-menu-item-checked');
31008         this.refreshValue();
31009         //if(this.el.dom.checked != this.checked){
31010         //    this.setValue(this.el.dom.checked);
31011        // }
31012     },
31013     
31014     // private
31015     refreshValue : function()
31016     {
31017         var val = '';
31018         this.viewEl.select('img',true).each(function(e,i,n)  {
31019             val += e.is(".x-menu-item-checked") ? String(n) : '';
31020         });
31021         this.setValue(val, true);
31022     },
31023
31024     /**
31025      * Sets the checked state of the checkbox.
31026      * On is always based on a string comparison between inputValue and the param.
31027      * @param {Boolean/String} value - the value to set 
31028      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31029      */
31030     setValue : function(v,suppressEvent){
31031         if (!this.el.dom) {
31032             return;
31033         }
31034         var old = this.el.dom.value ;
31035         this.el.dom.value = v;
31036         if (suppressEvent) {
31037             return ;
31038         }
31039          
31040         // update display..
31041         this.viewEl.select('img',true).each(function(e,i,n)  {
31042             
31043             var on = e.is(".x-menu-item-checked");
31044             var newv = v.indexOf(String(n)) > -1;
31045             if (on != newv) {
31046                 e.toggleClass('x-menu-item-checked');
31047             }
31048             
31049         });
31050         
31051         
31052         this.fireEvent('change', this, v, old);
31053         
31054         
31055     },
31056    
31057     // handle setting of hidden value by some other method!!?!?
31058     setFromHidden: function()
31059     {
31060         if(!this.el){
31061             return;
31062         }
31063         //console.log("SET FROM HIDDEN");
31064         //alert('setFrom hidden');
31065         this.setValue(this.el.dom.value);
31066     },
31067     
31068     onDestroy : function()
31069     {
31070         if(this.viewEl){
31071             Roo.get(this.viewEl).remove();
31072         }
31073          
31074         Roo.form.DayPicker.superclass.onDestroy.call(this);
31075     }
31076
31077 });/*
31078  * RooJS Library 1.1.1
31079  * Copyright(c) 2008-2011  Alan Knowles
31080  *
31081  * License - LGPL
31082  */
31083  
31084
31085 /**
31086  * @class Roo.form.ComboCheck
31087  * @extends Roo.form.ComboBox
31088  * A combobox for multiple select items.
31089  *
31090  * FIXME - could do with a reset button..
31091  * 
31092  * @constructor
31093  * Create a new ComboCheck
31094  * @param {Object} config Configuration options
31095  */
31096 Roo.form.ComboCheck = function(config){
31097     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31098     // should verify some data...
31099     // like
31100     // hiddenName = required..
31101     // displayField = required
31102     // valudField == required
31103     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31104     var _t = this;
31105     Roo.each(req, function(e) {
31106         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31107             throw "Roo.form.ComboCheck : missing value for: " + e;
31108         }
31109     });
31110     
31111     
31112 };
31113
31114 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31115      
31116      
31117     editable : false,
31118      
31119     selectedClass: 'x-menu-item-checked', 
31120     
31121     // private
31122     onRender : function(ct, position){
31123         var _t = this;
31124         
31125         
31126         
31127         if(!this.tpl){
31128             var cls = 'x-combo-list';
31129
31130             
31131             this.tpl =  new Roo.Template({
31132                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31133                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31134                    '<span>{' + this.displayField + '}</span>' +
31135                     '</div>' 
31136                 
31137             });
31138         }
31139  
31140         
31141         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31142         this.view.singleSelect = false;
31143         this.view.multiSelect = true;
31144         this.view.toggleSelect = true;
31145         this.pageTb.add(new Roo.Toolbar.Fill(), {
31146             
31147             text: 'Done',
31148             handler: function()
31149             {
31150                 _t.collapse();
31151             }
31152         });
31153     },
31154     
31155     onViewOver : function(e, t){
31156         // do nothing...
31157         return;
31158         
31159     },
31160     
31161     onViewClick : function(doFocus,index){
31162         return;
31163         
31164     },
31165     select: function () {
31166         //Roo.log("SELECT CALLED");
31167     },
31168      
31169     selectByValue : function(xv, scrollIntoView){
31170         var ar = this.getValueArray();
31171         var sels = [];
31172         
31173         Roo.each(ar, function(v) {
31174             if(v === undefined || v === null){
31175                 return;
31176             }
31177             var r = this.findRecord(this.valueField, v);
31178             if(r){
31179                 sels.push(this.store.indexOf(r))
31180                 
31181             }
31182         },this);
31183         this.view.select(sels);
31184         return false;
31185     },
31186     
31187     
31188     
31189     onSelect : function(record, index){
31190        // Roo.log("onselect Called");
31191        // this is only called by the clear button now..
31192         this.view.clearSelections();
31193         this.setValue('[]');
31194         if (this.value != this.valueBefore) {
31195             this.fireEvent('change', this, this.value, this.valueBefore);
31196             this.valueBefore = this.value;
31197         }
31198     },
31199     getValueArray : function()
31200     {
31201         var ar = [] ;
31202         
31203         try {
31204             //Roo.log(this.value);
31205             if (typeof(this.value) == 'undefined') {
31206                 return [];
31207             }
31208             var ar = Roo.decode(this.value);
31209             return  ar instanceof Array ? ar : []; //?? valid?
31210             
31211         } catch(e) {
31212             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31213             return [];
31214         }
31215          
31216     },
31217     expand : function ()
31218     {
31219         
31220         Roo.form.ComboCheck.superclass.expand.call(this);
31221         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31222         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31223         
31224
31225     },
31226     
31227     collapse : function(){
31228         Roo.form.ComboCheck.superclass.collapse.call(this);
31229         var sl = this.view.getSelectedIndexes();
31230         var st = this.store;
31231         var nv = [];
31232         var tv = [];
31233         var r;
31234         Roo.each(sl, function(i) {
31235             r = st.getAt(i);
31236             nv.push(r.get(this.valueField));
31237         },this);
31238         this.setValue(Roo.encode(nv));
31239         if (this.value != this.valueBefore) {
31240
31241             this.fireEvent('change', this, this.value, this.valueBefore);
31242             this.valueBefore = this.value;
31243         }
31244         
31245     },
31246     
31247     setValue : function(v){
31248         // Roo.log(v);
31249         this.value = v;
31250         
31251         var vals = this.getValueArray();
31252         var tv = [];
31253         Roo.each(vals, function(k) {
31254             var r = this.findRecord(this.valueField, k);
31255             if(r){
31256                 tv.push(r.data[this.displayField]);
31257             }else if(this.valueNotFoundText !== undefined){
31258                 tv.push( this.valueNotFoundText );
31259             }
31260         },this);
31261        // Roo.log(tv);
31262         
31263         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31264         this.hiddenField.value = v;
31265         this.value = v;
31266     }
31267     
31268 });/*
31269  * Based on:
31270  * Ext JS Library 1.1.1
31271  * Copyright(c) 2006-2007, Ext JS, LLC.
31272  *
31273  * Originally Released Under LGPL - original licence link has changed is not relivant.
31274  *
31275  * Fork - LGPL
31276  * <script type="text/javascript">
31277  */
31278  
31279 /**
31280  * @class Roo.form.Signature
31281  * @extends Roo.form.Field
31282  * Signature field.  
31283  * @constructor
31284  * 
31285  * @param {Object} config Configuration options
31286  */
31287
31288 Roo.form.Signature = function(config){
31289     Roo.form.Signature.superclass.constructor.call(this, config);
31290     
31291     this.addEvents({// not in used??
31292          /**
31293          * @event confirm
31294          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31295              * @param {Roo.form.Signature} combo This combo box
31296              */
31297         'confirm' : true,
31298         /**
31299          * @event reset
31300          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31301              * @param {Roo.form.ComboBox} combo This combo box
31302              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31303              */
31304         'reset' : true
31305     });
31306 };
31307
31308 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31309     /**
31310      * @cfg {Object} labels Label to use when rendering a form.
31311      * defaults to 
31312      * labels : { 
31313      *      clear : "Clear",
31314      *      confirm : "Confirm"
31315      *  }
31316      */
31317     labels : { 
31318         clear : "Clear",
31319         confirm : "Confirm"
31320     },
31321     /**
31322      * @cfg {Number} width The signature panel width (defaults to 300)
31323      */
31324     width: 300,
31325     /**
31326      * @cfg {Number} height The signature panel height (defaults to 100)
31327      */
31328     height : 100,
31329     /**
31330      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31331      */
31332     allowBlank : false,
31333     
31334     //private
31335     // {Object} signPanel The signature SVG panel element (defaults to {})
31336     signPanel : {},
31337     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31338     isMouseDown : false,
31339     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31340     isConfirmed : false,
31341     // {String} signatureTmp SVG mapping string (defaults to empty string)
31342     signatureTmp : '',
31343     
31344     
31345     defaultAutoCreate : { // modified by initCompnoent..
31346         tag: "input",
31347         type:"hidden"
31348     },
31349
31350     // private
31351     onRender : function(ct, position){
31352         
31353         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31354         
31355         this.wrap = this.el.wrap({
31356             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31357         });
31358         
31359         this.createToolbar(this);
31360         this.signPanel = this.wrap.createChild({
31361                 tag: 'div',
31362                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31363             }, this.el
31364         );
31365             
31366         this.svgID = Roo.id();
31367         this.svgEl = this.signPanel.createChild({
31368               xmlns : 'http://www.w3.org/2000/svg',
31369               tag : 'svg',
31370               id : this.svgID + "-svg",
31371               width: this.width,
31372               height: this.height,
31373               viewBox: '0 0 '+this.width+' '+this.height,
31374               cn : [
31375                 {
31376                     tag: "rect",
31377                     id: this.svgID + "-svg-r",
31378                     width: this.width,
31379                     height: this.height,
31380                     fill: "#ffa"
31381                 },
31382                 {
31383                     tag: "line",
31384                     id: this.svgID + "-svg-l",
31385                     x1: "0", // start
31386                     y1: (this.height*0.8), // start set the line in 80% of height
31387                     x2: this.width, // end
31388                     y2: (this.height*0.8), // end set the line in 80% of height
31389                     'stroke': "#666",
31390                     'stroke-width': "1",
31391                     'stroke-dasharray': "3",
31392                     'shape-rendering': "crispEdges",
31393                     'pointer-events': "none"
31394                 },
31395                 {
31396                     tag: "path",
31397                     id: this.svgID + "-svg-p",
31398                     'stroke': "navy",
31399                     'stroke-width': "3",
31400                     'fill': "none",
31401                     'pointer-events': 'none'
31402                 }
31403               ]
31404         });
31405         this.createSVG();
31406         this.svgBox = this.svgEl.dom.getScreenCTM();
31407     },
31408     createSVG : function(){ 
31409         var svg = this.signPanel;
31410         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31411         var t = this;
31412
31413         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31414         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31415         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31416         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31417         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31418         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31419         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31420         
31421     },
31422     isTouchEvent : function(e){
31423         return e.type.match(/^touch/);
31424     },
31425     getCoords : function (e) {
31426         var pt    = this.svgEl.dom.createSVGPoint();
31427         pt.x = e.clientX; 
31428         pt.y = e.clientY;
31429         if (this.isTouchEvent(e)) {
31430             pt.x =  e.targetTouches[0].clientX 
31431             pt.y = e.targetTouches[0].clientY;
31432         }
31433         var a = this.svgEl.dom.getScreenCTM();
31434         var b = a.inverse();
31435         var mx = pt.matrixTransform(b);
31436         return mx.x + ',' + mx.y;
31437     },
31438     //mouse event headler 
31439     down : function (e) {
31440         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31441         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31442         
31443         this.isMouseDown = true;
31444         
31445         e.preventDefault();
31446     },
31447     move : function (e) {
31448         if (this.isMouseDown) {
31449             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31450             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31451         }
31452         
31453         e.preventDefault();
31454     },
31455     up : function (e) {
31456         this.isMouseDown = false;
31457         var sp = this.signatureTmp.split(' ');
31458         
31459         if(sp.length > 1){
31460             if(!sp[sp.length-2].match(/^L/)){
31461                 sp.pop();
31462                 sp.pop();
31463                 sp.push("");
31464                 this.signatureTmp = sp.join(" ");
31465             }
31466         }
31467         if(this.getValue() != this.signatureTmp){
31468             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31469             this.isConfirmed = false;
31470         }
31471         e.preventDefault();
31472     },
31473     
31474     /**
31475      * Protected method that will not generally be called directly. It
31476      * is called when the editor creates its toolbar. Override this method if you need to
31477      * add custom toolbar buttons.
31478      * @param {HtmlEditor} editor
31479      */
31480     createToolbar : function(editor){
31481          function btn(id, toggle, handler){
31482             var xid = fid + '-'+ id ;
31483             return {
31484                 id : xid,
31485                 cmd : id,
31486                 cls : 'x-btn-icon x-edit-'+id,
31487                 enableToggle:toggle !== false,
31488                 scope: editor, // was editor...
31489                 handler:handler||editor.relayBtnCmd,
31490                 clickEvent:'mousedown',
31491                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31492                 tabIndex:-1
31493             };
31494         }
31495         
31496         
31497         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31498         this.tb = tb;
31499         this.tb.add(
31500            {
31501                 cls : ' x-signature-btn x-signature-'+id,
31502                 scope: editor, // was editor...
31503                 handler: this.reset,
31504                 clickEvent:'mousedown',
31505                 text: this.labels.clear
31506             },
31507             {
31508                  xtype : 'Fill',
31509                  xns: Roo.Toolbar
31510             }, 
31511             {
31512                 cls : '  x-signature-btn x-signature-'+id,
31513                 scope: editor, // was editor...
31514                 handler: this.confirmHandler,
31515                 clickEvent:'mousedown',
31516                 text: this.labels.confirm
31517             }
31518         );
31519     
31520     },
31521     //public
31522     /**
31523      * when user is clicked confirm then show this image.....
31524      * 
31525      * @return {String} Image Data URI
31526      */
31527     getImageDataURI : function(){
31528         var svg = this.svgEl.dom.parentNode.innerHTML;
31529         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31530         return src; 
31531     },
31532     /**
31533      * 
31534      * @return {Boolean} this.isConfirmed
31535      */
31536     getConfirmed : function(){
31537         return this.isConfirmed;
31538     },
31539     /**
31540      * 
31541      * @return {Number} this.width
31542      */
31543     getWidth : function(){
31544         return this.width;
31545     },
31546     /**
31547      * 
31548      * @return {Number} this.height
31549      */
31550     getHeight : function(){
31551         return this.height;
31552     },
31553     // private
31554     getSignature : function(){
31555         return this.signatureTmp;
31556     },
31557     // private
31558     reset : function(){
31559         this.signatureTmp = '';
31560         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31561         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31562         this.isConfirmed = false;
31563         Roo.form.Signature.superclass.reset.call(this);
31564     },
31565     setSignature : function(s){
31566         this.signatureTmp = s;
31567         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31568         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31569         this.setValue(s);
31570         this.isConfirmed = false;
31571         Roo.form.Signature.superclass.reset.call(this);
31572     }, 
31573     test : function(){
31574 //        Roo.log(this.signPanel.dom.contentWindow.up())
31575     },
31576     //private
31577     setConfirmed : function(){
31578         
31579         
31580         
31581 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31582     },
31583     // private
31584     confirmHandler : function(){
31585         if(!this.getSignature()){
31586             return;
31587         }
31588         
31589         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31590         this.setValue(this.getSignature());
31591         this.isConfirmed = true;
31592         
31593         this.fireEvent('confirm', this);
31594     },
31595     // private
31596     // Subclasses should provide the validation implementation by overriding this
31597     validateValue : function(value){
31598         if(this.allowBlank){
31599             return true;
31600         }
31601         
31602         if(this.isConfirmed){
31603             return true;
31604         }
31605         return false;
31606     }
31607 });/*
31608  * Based on:
31609  * Ext JS Library 1.1.1
31610  * Copyright(c) 2006-2007, Ext JS, LLC.
31611  *
31612  * Originally Released Under LGPL - original licence link has changed is not relivant.
31613  *
31614  * Fork - LGPL
31615  * <script type="text/javascript">
31616  */
31617  
31618
31619 /**
31620  * @class Roo.form.ComboBox
31621  * @extends Roo.form.TriggerField
31622  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31623  * @constructor
31624  * Create a new ComboBox.
31625  * @param {Object} config Configuration options
31626  */
31627 Roo.form.Select = function(config){
31628     Roo.form.Select.superclass.constructor.call(this, config);
31629      
31630 };
31631
31632 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31633     /**
31634      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31635      */
31636     /**
31637      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31638      * rendering into an Roo.Editor, defaults to false)
31639      */
31640     /**
31641      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31642      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31643      */
31644     /**
31645      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31646      */
31647     /**
31648      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31649      * the dropdown list (defaults to undefined, with no header element)
31650      */
31651
31652      /**
31653      * @cfg {String/Roo.Template} tpl The template to use to render the output
31654      */
31655      
31656     // private
31657     defaultAutoCreate : {tag: "select"  },
31658     /**
31659      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31660      */
31661     listWidth: undefined,
31662     /**
31663      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31664      * mode = 'remote' or 'text' if mode = 'local')
31665      */
31666     displayField: undefined,
31667     /**
31668      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31669      * mode = 'remote' or 'value' if mode = 'local'). 
31670      * Note: use of a valueField requires the user make a selection
31671      * in order for a value to be mapped.
31672      */
31673     valueField: undefined,
31674     
31675     
31676     /**
31677      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31678      * field's data value (defaults to the underlying DOM element's name)
31679      */
31680     hiddenName: undefined,
31681     /**
31682      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31683      */
31684     listClass: '',
31685     /**
31686      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31687      */
31688     selectedClass: 'x-combo-selected',
31689     /**
31690      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31691      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31692      * which displays a downward arrow icon).
31693      */
31694     triggerClass : 'x-form-arrow-trigger',
31695     /**
31696      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31697      */
31698     shadow:'sides',
31699     /**
31700      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31701      * anchor positions (defaults to 'tl-bl')
31702      */
31703     listAlign: 'tl-bl?',
31704     /**
31705      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31706      */
31707     maxHeight: 300,
31708     /**
31709      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31710      * query specified by the allQuery config option (defaults to 'query')
31711      */
31712     triggerAction: 'query',
31713     /**
31714      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31715      * (defaults to 4, does not apply if editable = false)
31716      */
31717     minChars : 4,
31718     /**
31719      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31720      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31721      */
31722     typeAhead: false,
31723     /**
31724      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31725      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31726      */
31727     queryDelay: 500,
31728     /**
31729      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31730      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31731      */
31732     pageSize: 0,
31733     /**
31734      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31735      * when editable = true (defaults to false)
31736      */
31737     selectOnFocus:false,
31738     /**
31739      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31740      */
31741     queryParam: 'query',
31742     /**
31743      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31744      * when mode = 'remote' (defaults to 'Loading...')
31745      */
31746     loadingText: 'Loading...',
31747     /**
31748      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31749      */
31750     resizable: false,
31751     /**
31752      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31753      */
31754     handleHeight : 8,
31755     /**
31756      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31757      * traditional select (defaults to true)
31758      */
31759     editable: true,
31760     /**
31761      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31762      */
31763     allQuery: '',
31764     /**
31765      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31766      */
31767     mode: 'remote',
31768     /**
31769      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31770      * listWidth has a higher value)
31771      */
31772     minListWidth : 70,
31773     /**
31774      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31775      * allow the user to set arbitrary text into the field (defaults to false)
31776      */
31777     forceSelection:false,
31778     /**
31779      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31780      * if typeAhead = true (defaults to 250)
31781      */
31782     typeAheadDelay : 250,
31783     /**
31784      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31785      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31786      */
31787     valueNotFoundText : undefined,
31788     
31789     /**
31790      * @cfg {String} defaultValue The value displayed after loading the store.
31791      */
31792     defaultValue: '',
31793     
31794     /**
31795      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31796      */
31797     blockFocus : false,
31798     
31799     /**
31800      * @cfg {Boolean} disableClear Disable showing of clear button.
31801      */
31802     disableClear : false,
31803     /**
31804      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31805      */
31806     alwaysQuery : false,
31807     
31808     //private
31809     addicon : false,
31810     editicon: false,
31811     
31812     // element that contains real text value.. (when hidden is used..)
31813      
31814     // private
31815     onRender : function(ct, position){
31816         Roo.form.Field.prototype.onRender.call(this, ct, position);
31817         
31818         if(this.store){
31819             this.store.on('beforeload', this.onBeforeLoad, this);
31820             this.store.on('load', this.onLoad, this);
31821             this.store.on('loadexception', this.onLoadException, this);
31822             this.store.load({});
31823         }
31824         
31825         
31826         
31827     },
31828
31829     // private
31830     initEvents : function(){
31831         //Roo.form.ComboBox.superclass.initEvents.call(this);
31832  
31833     },
31834
31835     onDestroy : function(){
31836        
31837         if(this.store){
31838             this.store.un('beforeload', this.onBeforeLoad, this);
31839             this.store.un('load', this.onLoad, this);
31840             this.store.un('loadexception', this.onLoadException, this);
31841         }
31842         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31843     },
31844
31845     // private
31846     fireKey : function(e){
31847         if(e.isNavKeyPress() && !this.list.isVisible()){
31848             this.fireEvent("specialkey", this, e);
31849         }
31850     },
31851
31852     // private
31853     onResize: function(w, h){
31854         
31855         return; 
31856     
31857         
31858     },
31859
31860     /**
31861      * Allow or prevent the user from directly editing the field text.  If false is passed,
31862      * the user will only be able to select from the items defined in the dropdown list.  This method
31863      * is the runtime equivalent of setting the 'editable' config option at config time.
31864      * @param {Boolean} value True to allow the user to directly edit the field text
31865      */
31866     setEditable : function(value){
31867          
31868     },
31869
31870     // private
31871     onBeforeLoad : function(){
31872         
31873         Roo.log("Select before load");
31874         return;
31875     
31876         this.innerList.update(this.loadingText ?
31877                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31878         //this.restrictHeight();
31879         this.selectedIndex = -1;
31880     },
31881
31882     // private
31883     onLoad : function(){
31884
31885     
31886         var dom = this.el.dom;
31887         dom.innerHTML = '';
31888          var od = dom.ownerDocument;
31889          
31890         if (this.emptyText) {
31891             var op = od.createElement('option');
31892             op.setAttribute('value', '');
31893             op.innerHTML = String.format('{0}', this.emptyText);
31894             dom.appendChild(op);
31895         }
31896         if(this.store.getCount() > 0){
31897            
31898             var vf = this.valueField;
31899             var df = this.displayField;
31900             this.store.data.each(function(r) {
31901                 // which colmsn to use... testing - cdoe / title..
31902                 var op = od.createElement('option');
31903                 op.setAttribute('value', r.data[vf]);
31904                 op.innerHTML = String.format('{0}', r.data[df]);
31905                 dom.appendChild(op);
31906             });
31907             if (typeof(this.defaultValue != 'undefined')) {
31908                 this.setValue(this.defaultValue);
31909             }
31910             
31911              
31912         }else{
31913             //this.onEmptyResults();
31914         }
31915         //this.el.focus();
31916     },
31917     // private
31918     onLoadException : function()
31919     {
31920         dom.innerHTML = '';
31921             
31922         Roo.log("Select on load exception");
31923         return;
31924     
31925         this.collapse();
31926         Roo.log(this.store.reader.jsonData);
31927         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31928             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31929         }
31930         
31931         
31932     },
31933     // private
31934     onTypeAhead : function(){
31935          
31936     },
31937
31938     // private
31939     onSelect : function(record, index){
31940         Roo.log('on select?');
31941         return;
31942         if(this.fireEvent('beforeselect', this, record, index) !== false){
31943             this.setFromData(index > -1 ? record.data : false);
31944             this.collapse();
31945             this.fireEvent('select', this, record, index);
31946         }
31947     },
31948
31949     /**
31950      * Returns the currently selected field value or empty string if no value is set.
31951      * @return {String} value The selected value
31952      */
31953     getValue : function(){
31954         var dom = this.el.dom;
31955         this.value = dom.options[dom.selectedIndex].value;
31956         return this.value;
31957         
31958     },
31959
31960     /**
31961      * Clears any text/value currently set in the field
31962      */
31963     clearValue : function(){
31964         this.value = '';
31965         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31966         
31967     },
31968
31969     /**
31970      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31971      * will be displayed in the field.  If the value does not match the data value of an existing item,
31972      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31973      * Otherwise the field will be blank (although the value will still be set).
31974      * @param {String} value The value to match
31975      */
31976     setValue : function(v){
31977         var d = this.el.dom;
31978         for (var i =0; i < d.options.length;i++) {
31979             if (v == d.options[i].value) {
31980                 d.selectedIndex = i;
31981                 this.value = v;
31982                 return;
31983             }
31984         }
31985         this.clearValue();
31986     },
31987     /**
31988      * @property {Object} the last set data for the element
31989      */
31990     
31991     lastData : false,
31992     /**
31993      * Sets the value of the field based on a object which is related to the record format for the store.
31994      * @param {Object} value the value to set as. or false on reset?
31995      */
31996     setFromData : function(o){
31997         Roo.log('setfrom data?');
31998          
31999         
32000         
32001     },
32002     // private
32003     reset : function(){
32004         this.clearValue();
32005     },
32006     // private
32007     findRecord : function(prop, value){
32008         
32009         return false;
32010     
32011         var record;
32012         if(this.store.getCount() > 0){
32013             this.store.each(function(r){
32014                 if(r.data[prop] == value){
32015                     record = r;
32016                     return false;
32017                 }
32018                 return true;
32019             });
32020         }
32021         return record;
32022     },
32023     
32024     getName: function()
32025     {
32026         // returns hidden if it's set..
32027         if (!this.rendered) {return ''};
32028         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32029         
32030     },
32031      
32032
32033     
32034
32035     // private
32036     onEmptyResults : function(){
32037         Roo.log('empty results');
32038         //this.collapse();
32039     },
32040
32041     /**
32042      * Returns true if the dropdown list is expanded, else false.
32043      */
32044     isExpanded : function(){
32045         return false;
32046     },
32047
32048     /**
32049      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32050      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32051      * @param {String} value The data value of the item to select
32052      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32053      * selected item if it is not currently in view (defaults to true)
32054      * @return {Boolean} True if the value matched an item in the list, else false
32055      */
32056     selectByValue : function(v, scrollIntoView){
32057         Roo.log('select By Value');
32058         return false;
32059     
32060         if(v !== undefined && v !== null){
32061             var r = this.findRecord(this.valueField || this.displayField, v);
32062             if(r){
32063                 this.select(this.store.indexOf(r), scrollIntoView);
32064                 return true;
32065             }
32066         }
32067         return false;
32068     },
32069
32070     /**
32071      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32073      * @param {Number} index The zero-based index of the list item to select
32074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32075      * selected item if it is not currently in view (defaults to true)
32076      */
32077     select : function(index, scrollIntoView){
32078         Roo.log('select ');
32079         return  ;
32080         
32081         this.selectedIndex = index;
32082         this.view.select(index);
32083         if(scrollIntoView !== false){
32084             var el = this.view.getNode(index);
32085             if(el){
32086                 this.innerList.scrollChildIntoView(el, false);
32087             }
32088         }
32089     },
32090
32091       
32092
32093     // private
32094     validateBlur : function(){
32095         
32096         return;
32097         
32098     },
32099
32100     // private
32101     initQuery : function(){
32102         this.doQuery(this.getRawValue());
32103     },
32104
32105     // private
32106     doForce : function(){
32107         if(this.el.dom.value.length > 0){
32108             this.el.dom.value =
32109                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32110              
32111         }
32112     },
32113
32114     /**
32115      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32116      * query allowing the query action to be canceled if needed.
32117      * @param {String} query The SQL query to execute
32118      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32119      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32120      * saved in the current store (defaults to false)
32121      */
32122     doQuery : function(q, forceAll){
32123         
32124         Roo.log('doQuery?');
32125         if(q === undefined || q === null){
32126             q = '';
32127         }
32128         var qe = {
32129             query: q,
32130             forceAll: forceAll,
32131             combo: this,
32132             cancel:false
32133         };
32134         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32135             return false;
32136         }
32137         q = qe.query;
32138         forceAll = qe.forceAll;
32139         if(forceAll === true || (q.length >= this.minChars)){
32140             if(this.lastQuery != q || this.alwaysQuery){
32141                 this.lastQuery = q;
32142                 if(this.mode == 'local'){
32143                     this.selectedIndex = -1;
32144                     if(forceAll){
32145                         this.store.clearFilter();
32146                     }else{
32147                         this.store.filter(this.displayField, q);
32148                     }
32149                     this.onLoad();
32150                 }else{
32151                     this.store.baseParams[this.queryParam] = q;
32152                     this.store.load({
32153                         params: this.getParams(q)
32154                     });
32155                     this.expand();
32156                 }
32157             }else{
32158                 this.selectedIndex = -1;
32159                 this.onLoad();   
32160             }
32161         }
32162     },
32163
32164     // private
32165     getParams : function(q){
32166         var p = {};
32167         //p[this.queryParam] = q;
32168         if(this.pageSize){
32169             p.start = 0;
32170             p.limit = this.pageSize;
32171         }
32172         return p;
32173     },
32174
32175     /**
32176      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32177      */
32178     collapse : function(){
32179         
32180     },
32181
32182     // private
32183     collapseIf : function(e){
32184         
32185     },
32186
32187     /**
32188      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32189      */
32190     expand : function(){
32191         
32192     } ,
32193
32194     // private
32195      
32196
32197     /** 
32198     * @cfg {Boolean} grow 
32199     * @hide 
32200     */
32201     /** 
32202     * @cfg {Number} growMin 
32203     * @hide 
32204     */
32205     /** 
32206     * @cfg {Number} growMax 
32207     * @hide 
32208     */
32209     /**
32210      * @hide
32211      * @method autoSize
32212      */
32213     
32214     setWidth : function()
32215     {
32216         
32217     },
32218     getResizeEl : function(){
32219         return this.el;
32220     }
32221 });//<script type="text/javasscript">
32222  
32223
32224 /**
32225  * @class Roo.DDView
32226  * A DnD enabled version of Roo.View.
32227  * @param {Element/String} container The Element in which to create the View.
32228  * @param {String} tpl The template string used to create the markup for each element of the View
32229  * @param {Object} config The configuration properties. These include all the config options of
32230  * {@link Roo.View} plus some specific to this class.<br>
32231  * <p>
32232  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32233  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32234  * <p>
32235  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32236 .x-view-drag-insert-above {
32237         border-top:1px dotted #3366cc;
32238 }
32239 .x-view-drag-insert-below {
32240         border-bottom:1px dotted #3366cc;
32241 }
32242 </code></pre>
32243  * 
32244  */
32245  
32246 Roo.DDView = function(container, tpl, config) {
32247     Roo.DDView.superclass.constructor.apply(this, arguments);
32248     this.getEl().setStyle("outline", "0px none");
32249     this.getEl().unselectable();
32250     if (this.dragGroup) {
32251                 this.setDraggable(this.dragGroup.split(","));
32252     }
32253     if (this.dropGroup) {
32254                 this.setDroppable(this.dropGroup.split(","));
32255     }
32256     if (this.deletable) {
32257         this.setDeletable();
32258     }
32259     this.isDirtyFlag = false;
32260         this.addEvents({
32261                 "drop" : true
32262         });
32263 };
32264
32265 Roo.extend(Roo.DDView, Roo.View, {
32266 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32267 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32268 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32269 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32270
32271         isFormField: true,
32272
32273         reset: Roo.emptyFn,
32274         
32275         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32276
32277         validate: function() {
32278                 return true;
32279         },
32280         
32281         destroy: function() {
32282                 this.purgeListeners();
32283                 this.getEl.removeAllListeners();
32284                 this.getEl().remove();
32285                 if (this.dragZone) {
32286                         if (this.dragZone.destroy) {
32287                                 this.dragZone.destroy();
32288                         }
32289                 }
32290                 if (this.dropZone) {
32291                         if (this.dropZone.destroy) {
32292                                 this.dropZone.destroy();
32293                         }
32294                 }
32295         },
32296
32297 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32298         getName: function() {
32299                 return this.name;
32300         },
32301
32302 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32303         setValue: function(v) {
32304                 if (!this.store) {
32305                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32306                 }
32307                 var data = {};
32308                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32309                 this.store.proxy = new Roo.data.MemoryProxy(data);
32310                 this.store.load();
32311         },
32312
32313 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32314         getValue: function() {
32315                 var result = '(';
32316                 this.store.each(function(rec) {
32317                         result += rec.id + ',';
32318                 });
32319                 return result.substr(0, result.length - 1) + ')';
32320         },
32321         
32322         getIds: function() {
32323                 var i = 0, result = new Array(this.store.getCount());
32324                 this.store.each(function(rec) {
32325                         result[i++] = rec.id;
32326                 });
32327                 return result;
32328         },
32329         
32330         isDirty: function() {
32331                 return this.isDirtyFlag;
32332         },
32333
32334 /**
32335  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32336  *      whole Element becomes the target, and this causes the drop gesture to append.
32337  */
32338     getTargetFromEvent : function(e) {
32339                 var target = e.getTarget();
32340                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32341                 target = target.parentNode;
32342                 }
32343                 if (!target) {
32344                         target = this.el.dom.lastChild || this.el.dom;
32345                 }
32346                 return target;
32347     },
32348
32349 /**
32350  *      Create the drag data which consists of an object which has the property "ddel" as
32351  *      the drag proxy element. 
32352  */
32353     getDragData : function(e) {
32354         var target = this.findItemFromChild(e.getTarget());
32355                 if(target) {
32356                         this.handleSelection(e);
32357                         var selNodes = this.getSelectedNodes();
32358             var dragData = {
32359                 source: this,
32360                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32361                 nodes: selNodes,
32362                 records: []
32363                         };
32364                         var selectedIndices = this.getSelectedIndexes();
32365                         for (var i = 0; i < selectedIndices.length; i++) {
32366                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32367                         }
32368                         if (selNodes.length == 1) {
32369                                 dragData.ddel = target.cloneNode(true); // the div element
32370                         } else {
32371                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32372                                 div.className = 'multi-proxy';
32373                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32374                                         div.appendChild(selNodes[i].cloneNode(true));
32375                                 }
32376                                 dragData.ddel = div;
32377                         }
32378             //console.log(dragData)
32379             //console.log(dragData.ddel.innerHTML)
32380                         return dragData;
32381                 }
32382         //console.log('nodragData')
32383                 return false;
32384     },
32385     
32386 /**     Specify to which ddGroup items in this DDView may be dragged. */
32387     setDraggable: function(ddGroup) {
32388         if (ddGroup instanceof Array) {
32389                 Roo.each(ddGroup, this.setDraggable, this);
32390                 return;
32391         }
32392         if (this.dragZone) {
32393                 this.dragZone.addToGroup(ddGroup);
32394         } else {
32395                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32396                                 containerScroll: true,
32397                                 ddGroup: ddGroup 
32398
32399                         });
32400 //                      Draggability implies selection. DragZone's mousedown selects the element.
32401                         if (!this.multiSelect) { this.singleSelect = true; }
32402
32403 //                      Wire the DragZone's handlers up to methods in *this*
32404                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32405                 }
32406     },
32407
32408 /**     Specify from which ddGroup this DDView accepts drops. */
32409     setDroppable: function(ddGroup) {
32410         if (ddGroup instanceof Array) {
32411                 Roo.each(ddGroup, this.setDroppable, this);
32412                 return;
32413         }
32414         if (this.dropZone) {
32415                 this.dropZone.addToGroup(ddGroup);
32416         } else {
32417                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32418                                 containerScroll: true,
32419                                 ddGroup: ddGroup
32420                         });
32421
32422 //                      Wire the DropZone's handlers up to methods in *this*
32423                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32424                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32425                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32426                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32427                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32428                 }
32429     },
32430
32431 /**     Decide whether to drop above or below a View node. */
32432     getDropPoint : function(e, n, dd){
32433         if (n == this.el.dom) { return "above"; }
32434                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32435                 var c = t + (b - t) / 2;
32436                 var y = Roo.lib.Event.getPageY(e);
32437                 if(y <= c) {
32438                         return "above";
32439                 }else{
32440                         return "below";
32441                 }
32442     },
32443
32444     onNodeEnter : function(n, dd, e, data){
32445                 return false;
32446     },
32447     
32448     onNodeOver : function(n, dd, e, data){
32449                 var pt = this.getDropPoint(e, n, dd);
32450                 // set the insert point style on the target node
32451                 var dragElClass = this.dropNotAllowed;
32452                 if (pt) {
32453                         var targetElClass;
32454                         if (pt == "above"){
32455                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32456                                 targetElClass = "x-view-drag-insert-above";
32457                         } else {
32458                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32459                                 targetElClass = "x-view-drag-insert-below";
32460                         }
32461                         if (this.lastInsertClass != targetElClass){
32462                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32463                                 this.lastInsertClass = targetElClass;
32464                         }
32465                 }
32466                 return dragElClass;
32467         },
32468
32469     onNodeOut : function(n, dd, e, data){
32470                 this.removeDropIndicators(n);
32471     },
32472
32473     onNodeDrop : function(n, dd, e, data){
32474         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32475                 return false;
32476         }
32477         var pt = this.getDropPoint(e, n, dd);
32478                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32479                 if (pt == "below") { insertAt++; }
32480                 for (var i = 0; i < data.records.length; i++) {
32481                         var r = data.records[i];
32482                         var dup = this.store.getById(r.id);
32483                         if (dup && (dd != this.dragZone)) {
32484                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32485                         } else {
32486                                 if (data.copy) {
32487                                         this.store.insert(insertAt++, r.copy());
32488                                 } else {
32489                                         data.source.isDirtyFlag = true;
32490                                         r.store.remove(r);
32491                                         this.store.insert(insertAt++, r);
32492                                 }
32493                                 this.isDirtyFlag = true;
32494                         }
32495                 }
32496                 this.dragZone.cachedTarget = null;
32497                 return true;
32498     },
32499
32500     removeDropIndicators : function(n){
32501                 if(n){
32502                         Roo.fly(n).removeClass([
32503                                 "x-view-drag-insert-above",
32504                                 "x-view-drag-insert-below"]);
32505                         this.lastInsertClass = "_noclass";
32506                 }
32507     },
32508
32509 /**
32510  *      Utility method. Add a delete option to the DDView's context menu.
32511  *      @param {String} imageUrl The URL of the "delete" icon image.
32512  */
32513         setDeletable: function(imageUrl) {
32514                 if (!this.singleSelect && !this.multiSelect) {
32515                         this.singleSelect = true;
32516                 }
32517                 var c = this.getContextMenu();
32518                 this.contextMenu.on("itemclick", function(item) {
32519                         switch (item.id) {
32520                                 case "delete":
32521                                         this.remove(this.getSelectedIndexes());
32522                                         break;
32523                         }
32524                 }, this);
32525                 this.contextMenu.add({
32526                         icon: imageUrl,
32527                         id: "delete",
32528                         text: 'Delete'
32529                 });
32530         },
32531         
32532 /**     Return the context menu for this DDView. */
32533         getContextMenu: function() {
32534                 if (!this.contextMenu) {
32535 //                      Create the View's context menu
32536                         this.contextMenu = new Roo.menu.Menu({
32537                                 id: this.id + "-contextmenu"
32538                         });
32539                         this.el.on("contextmenu", this.showContextMenu, this);
32540                 }
32541                 return this.contextMenu;
32542         },
32543         
32544         disableContextMenu: function() {
32545                 if (this.contextMenu) {
32546                         this.el.un("contextmenu", this.showContextMenu, this);
32547                 }
32548         },
32549
32550         showContextMenu: function(e, item) {
32551         item = this.findItemFromChild(e.getTarget());
32552                 if (item) {
32553                         e.stopEvent();
32554                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32555                         this.contextMenu.showAt(e.getXY());
32556             }
32557     },
32558
32559 /**
32560  *      Remove {@link Roo.data.Record}s at the specified indices.
32561  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32562  */
32563     remove: function(selectedIndices) {
32564                 selectedIndices = [].concat(selectedIndices);
32565                 for (var i = 0; i < selectedIndices.length; i++) {
32566                         var rec = this.store.getAt(selectedIndices[i]);
32567                         this.store.remove(rec);
32568                 }
32569     },
32570
32571 /**
32572  *      Double click fires the event, but also, if this is draggable, and there is only one other
32573  *      related DropZone, it transfers the selected node.
32574  */
32575     onDblClick : function(e){
32576         var item = this.findItemFromChild(e.getTarget());
32577         if(item){
32578             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32579                 return false;
32580             }
32581             if (this.dragGroup) {
32582                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32583                     while (targets.indexOf(this.dropZone) > -1) {
32584                             targets.remove(this.dropZone);
32585                                 }
32586                     if (targets.length == 1) {
32587                                         this.dragZone.cachedTarget = null;
32588                         var el = Roo.get(targets[0].getEl());
32589                         var box = el.getBox(true);
32590                         targets[0].onNodeDrop(el.dom, {
32591                                 target: el.dom,
32592                                 xy: [box.x, box.y + box.height - 1]
32593                         }, null, this.getDragData(e));
32594                     }
32595                 }
32596         }
32597     },
32598     
32599     handleSelection: function(e) {
32600                 this.dragZone.cachedTarget = null;
32601         var item = this.findItemFromChild(e.getTarget());
32602         if (!item) {
32603                 this.clearSelections(true);
32604                 return;
32605         }
32606                 if (item && (this.multiSelect || this.singleSelect)){
32607                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32608                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32609                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32610                                 this.unselect(item);
32611                         } else {
32612                                 this.select(item, this.multiSelect && e.ctrlKey);
32613                                 this.lastSelection = item;
32614                         }
32615                 }
32616     },
32617
32618     onItemClick : function(item, index, e){
32619                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32620                         return false;
32621                 }
32622                 return true;
32623     },
32624
32625     unselect : function(nodeInfo, suppressEvent){
32626                 var node = this.getNode(nodeInfo);
32627                 if(node && this.isSelected(node)){
32628                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32629                                 Roo.fly(node).removeClass(this.selectedClass);
32630                                 this.selections.remove(node);
32631                                 if(!suppressEvent){
32632                                         this.fireEvent("selectionchange", this, this.selections);
32633                                 }
32634                         }
32635                 }
32636     }
32637 });
32638 /*
32639  * Based on:
32640  * Ext JS Library 1.1.1
32641  * Copyright(c) 2006-2007, Ext JS, LLC.
32642  *
32643  * Originally Released Under LGPL - original licence link has changed is not relivant.
32644  *
32645  * Fork - LGPL
32646  * <script type="text/javascript">
32647  */
32648  
32649 /**
32650  * @class Roo.LayoutManager
32651  * @extends Roo.util.Observable
32652  * Base class for layout managers.
32653  */
32654 Roo.LayoutManager = function(container, config){
32655     Roo.LayoutManager.superclass.constructor.call(this);
32656     this.el = Roo.get(container);
32657     // ie scrollbar fix
32658     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32659         document.body.scroll = "no";
32660     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32661         this.el.position('relative');
32662     }
32663     this.id = this.el.id;
32664     this.el.addClass("x-layout-container");
32665     /** false to disable window resize monitoring @type Boolean */
32666     this.monitorWindowResize = true;
32667     this.regions = {};
32668     this.addEvents({
32669         /**
32670          * @event layout
32671          * Fires when a layout is performed. 
32672          * @param {Roo.LayoutManager} this
32673          */
32674         "layout" : true,
32675         /**
32676          * @event regionresized
32677          * Fires when the user resizes a region. 
32678          * @param {Roo.LayoutRegion} region The resized region
32679          * @param {Number} newSize The new size (width for east/west, height for north/south)
32680          */
32681         "regionresized" : true,
32682         /**
32683          * @event regioncollapsed
32684          * Fires when a region is collapsed. 
32685          * @param {Roo.LayoutRegion} region The collapsed region
32686          */
32687         "regioncollapsed" : true,
32688         /**
32689          * @event regionexpanded
32690          * Fires when a region is expanded.  
32691          * @param {Roo.LayoutRegion} region The expanded region
32692          */
32693         "regionexpanded" : true
32694     });
32695     this.updating = false;
32696     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32697 };
32698
32699 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32700     /**
32701      * Returns true if this layout is currently being updated
32702      * @return {Boolean}
32703      */
32704     isUpdating : function(){
32705         return this.updating; 
32706     },
32707     
32708     /**
32709      * Suspend the LayoutManager from doing auto-layouts while
32710      * making multiple add or remove calls
32711      */
32712     beginUpdate : function(){
32713         this.updating = true;    
32714     },
32715     
32716     /**
32717      * Restore auto-layouts and optionally disable the manager from performing a layout
32718      * @param {Boolean} noLayout true to disable a layout update 
32719      */
32720     endUpdate : function(noLayout){
32721         this.updating = false;
32722         if(!noLayout){
32723             this.layout();
32724         }    
32725     },
32726     
32727     layout: function(){
32728         
32729     },
32730     
32731     onRegionResized : function(region, newSize){
32732         this.fireEvent("regionresized", region, newSize);
32733         this.layout();
32734     },
32735     
32736     onRegionCollapsed : function(region){
32737         this.fireEvent("regioncollapsed", region);
32738     },
32739     
32740     onRegionExpanded : function(region){
32741         this.fireEvent("regionexpanded", region);
32742     },
32743         
32744     /**
32745      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32746      * performs box-model adjustments.
32747      * @return {Object} The size as an object {width: (the width), height: (the height)}
32748      */
32749     getViewSize : function(){
32750         var size;
32751         if(this.el.dom != document.body){
32752             size = this.el.getSize();
32753         }else{
32754             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32755         }
32756         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32757         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32758         return size;
32759     },
32760     
32761     /**
32762      * Returns the Element this layout is bound to.
32763      * @return {Roo.Element}
32764      */
32765     getEl : function(){
32766         return this.el;
32767     },
32768     
32769     /**
32770      * Returns the specified region.
32771      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32772      * @return {Roo.LayoutRegion}
32773      */
32774     getRegion : function(target){
32775         return this.regions[target.toLowerCase()];
32776     },
32777     
32778     onWindowResize : function(){
32779         if(this.monitorWindowResize){
32780             this.layout();
32781         }
32782     }
32783 });/*
32784  * Based on:
32785  * Ext JS Library 1.1.1
32786  * Copyright(c) 2006-2007, Ext JS, LLC.
32787  *
32788  * Originally Released Under LGPL - original licence link has changed is not relivant.
32789  *
32790  * Fork - LGPL
32791  * <script type="text/javascript">
32792  */
32793 /**
32794  * @class Roo.BorderLayout
32795  * @extends Roo.LayoutManager
32796  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32797  * please see: <br><br>
32798  * <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>
32799  * <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>
32800  * Example:
32801  <pre><code>
32802  var layout = new Roo.BorderLayout(document.body, {
32803     north: {
32804         initialSize: 25,
32805         titlebar: false
32806     },
32807     west: {
32808         split:true,
32809         initialSize: 200,
32810         minSize: 175,
32811         maxSize: 400,
32812         titlebar: true,
32813         collapsible: true
32814     },
32815     east: {
32816         split:true,
32817         initialSize: 202,
32818         minSize: 175,
32819         maxSize: 400,
32820         titlebar: true,
32821         collapsible: true
32822     },
32823     south: {
32824         split:true,
32825         initialSize: 100,
32826         minSize: 100,
32827         maxSize: 200,
32828         titlebar: true,
32829         collapsible: true
32830     },
32831     center: {
32832         titlebar: true,
32833         autoScroll:true,
32834         resizeTabs: true,
32835         minTabWidth: 50,
32836         preferredTabWidth: 150
32837     }
32838 });
32839
32840 // shorthand
32841 var CP = Roo.ContentPanel;
32842
32843 layout.beginUpdate();
32844 layout.add("north", new CP("north", "North"));
32845 layout.add("south", new CP("south", {title: "South", closable: true}));
32846 layout.add("west", new CP("west", {title: "West"}));
32847 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32848 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32849 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32850 layout.getRegion("center").showPanel("center1");
32851 layout.endUpdate();
32852 </code></pre>
32853
32854 <b>The container the layout is rendered into can be either the body element or any other element.
32855 If it is not the body element, the container needs to either be an absolute positioned element,
32856 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32857 the container size if it is not the body element.</b>
32858
32859 * @constructor
32860 * Create a new BorderLayout
32861 * @param {String/HTMLElement/Element} container The container this layout is bound to
32862 * @param {Object} config Configuration options
32863  */
32864 Roo.BorderLayout = function(container, config){
32865     config = config || {};
32866     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32867     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32868     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32869         var target = this.factory.validRegions[i];
32870         if(config[target]){
32871             this.addRegion(target, config[target]);
32872         }
32873     }
32874 };
32875
32876 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32877     /**
32878      * Creates and adds a new region if it doesn't already exist.
32879      * @param {String} target The target region key (north, south, east, west or center).
32880      * @param {Object} config The regions config object
32881      * @return {BorderLayoutRegion} The new region
32882      */
32883     addRegion : function(target, config){
32884         if(!this.regions[target]){
32885             var r = this.factory.create(target, this, config);
32886             this.bindRegion(target, r);
32887         }
32888         return this.regions[target];
32889     },
32890
32891     // private (kinda)
32892     bindRegion : function(name, r){
32893         this.regions[name] = r;
32894         r.on("visibilitychange", this.layout, this);
32895         r.on("paneladded", this.layout, this);
32896         r.on("panelremoved", this.layout, this);
32897         r.on("invalidated", this.layout, this);
32898         r.on("resized", this.onRegionResized, this);
32899         r.on("collapsed", this.onRegionCollapsed, this);
32900         r.on("expanded", this.onRegionExpanded, this);
32901     },
32902
32903     /**
32904      * Performs a layout update.
32905      */
32906     layout : function(){
32907         if(this.updating) return;
32908         var size = this.getViewSize();
32909         var w = size.width;
32910         var h = size.height;
32911         var centerW = w;
32912         var centerH = h;
32913         var centerY = 0;
32914         var centerX = 0;
32915         //var x = 0, y = 0;
32916
32917         var rs = this.regions;
32918         var north = rs["north"];
32919         var south = rs["south"]; 
32920         var west = rs["west"];
32921         var east = rs["east"];
32922         var center = rs["center"];
32923         //if(this.hideOnLayout){ // not supported anymore
32924             //c.el.setStyle("display", "none");
32925         //}
32926         if(north && north.isVisible()){
32927             var b = north.getBox();
32928             var m = north.getMargins();
32929             b.width = w - (m.left+m.right);
32930             b.x = m.left;
32931             b.y = m.top;
32932             centerY = b.height + b.y + m.bottom;
32933             centerH -= centerY;
32934             north.updateBox(this.safeBox(b));
32935         }
32936         if(south && south.isVisible()){
32937             var b = south.getBox();
32938             var m = south.getMargins();
32939             b.width = w - (m.left+m.right);
32940             b.x = m.left;
32941             var totalHeight = (b.height + m.top + m.bottom);
32942             b.y = h - totalHeight + m.top;
32943             centerH -= totalHeight;
32944             south.updateBox(this.safeBox(b));
32945         }
32946         if(west && west.isVisible()){
32947             var b = west.getBox();
32948             var m = west.getMargins();
32949             b.height = centerH - (m.top+m.bottom);
32950             b.x = m.left;
32951             b.y = centerY + m.top;
32952             var totalWidth = (b.width + m.left + m.right);
32953             centerX += totalWidth;
32954             centerW -= totalWidth;
32955             west.updateBox(this.safeBox(b));
32956         }
32957         if(east && east.isVisible()){
32958             var b = east.getBox();
32959             var m = east.getMargins();
32960             b.height = centerH - (m.top+m.bottom);
32961             var totalWidth = (b.width + m.left + m.right);
32962             b.x = w - totalWidth + m.left;
32963             b.y = centerY + m.top;
32964             centerW -= totalWidth;
32965             east.updateBox(this.safeBox(b));
32966         }
32967         if(center){
32968             var m = center.getMargins();
32969             var centerBox = {
32970                 x: centerX + m.left,
32971                 y: centerY + m.top,
32972                 width: centerW - (m.left+m.right),
32973                 height: centerH - (m.top+m.bottom)
32974             };
32975             //if(this.hideOnLayout){
32976                 //center.el.setStyle("display", "block");
32977             //}
32978             center.updateBox(this.safeBox(centerBox));
32979         }
32980         this.el.repaint();
32981         this.fireEvent("layout", this);
32982     },
32983
32984     // private
32985     safeBox : function(box){
32986         box.width = Math.max(0, box.width);
32987         box.height = Math.max(0, box.height);
32988         return box;
32989     },
32990
32991     /**
32992      * Adds a ContentPanel (or subclass) to this layout.
32993      * @param {String} target The target region key (north, south, east, west or center).
32994      * @param {Roo.ContentPanel} panel The panel to add
32995      * @return {Roo.ContentPanel} The added panel
32996      */
32997     add : function(target, panel){
32998          
32999         target = target.toLowerCase();
33000         return this.regions[target].add(panel);
33001     },
33002
33003     /**
33004      * Remove a ContentPanel (or subclass) to this layout.
33005      * @param {String} target The target region key (north, south, east, west or center).
33006      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33007      * @return {Roo.ContentPanel} The removed panel
33008      */
33009     remove : function(target, panel){
33010         target = target.toLowerCase();
33011         return this.regions[target].remove(panel);
33012     },
33013
33014     /**
33015      * Searches all regions for a panel with the specified id
33016      * @param {String} panelId
33017      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33018      */
33019     findPanel : function(panelId){
33020         var rs = this.regions;
33021         for(var target in rs){
33022             if(typeof rs[target] != "function"){
33023                 var p = rs[target].getPanel(panelId);
33024                 if(p){
33025                     return p;
33026                 }
33027             }
33028         }
33029         return null;
33030     },
33031
33032     /**
33033      * Searches all regions for a panel with the specified id and activates (shows) it.
33034      * @param {String/ContentPanel} panelId The panels id or the panel itself
33035      * @return {Roo.ContentPanel} The shown panel or null
33036      */
33037     showPanel : function(panelId) {
33038       var rs = this.regions;
33039       for(var target in rs){
33040          var r = rs[target];
33041          if(typeof r != "function"){
33042             if(r.hasPanel(panelId)){
33043                return r.showPanel(panelId);
33044             }
33045          }
33046       }
33047       return null;
33048    },
33049
33050    /**
33051      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33052      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33053      */
33054     restoreState : function(provider){
33055         if(!provider){
33056             provider = Roo.state.Manager;
33057         }
33058         var sm = new Roo.LayoutStateManager();
33059         sm.init(this, provider);
33060     },
33061
33062     /**
33063      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33064      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33065      * a valid ContentPanel config object.  Example:
33066      * <pre><code>
33067 // Create the main layout
33068 var layout = new Roo.BorderLayout('main-ct', {
33069     west: {
33070         split:true,
33071         minSize: 175,
33072         titlebar: true
33073     },
33074     center: {
33075         title:'Components'
33076     }
33077 }, 'main-ct');
33078
33079 // Create and add multiple ContentPanels at once via configs
33080 layout.batchAdd({
33081    west: {
33082        id: 'source-files',
33083        autoCreate:true,
33084        title:'Ext Source Files',
33085        autoScroll:true,
33086        fitToFrame:true
33087    },
33088    center : {
33089        el: cview,
33090        autoScroll:true,
33091        fitToFrame:true,
33092        toolbar: tb,
33093        resizeEl:'cbody'
33094    }
33095 });
33096 </code></pre>
33097      * @param {Object} regions An object containing ContentPanel configs by region name
33098      */
33099     batchAdd : function(regions){
33100         this.beginUpdate();
33101         for(var rname in regions){
33102             var lr = this.regions[rname];
33103             if(lr){
33104                 this.addTypedPanels(lr, regions[rname]);
33105             }
33106         }
33107         this.endUpdate();
33108     },
33109
33110     // private
33111     addTypedPanels : function(lr, ps){
33112         if(typeof ps == 'string'){
33113             lr.add(new Roo.ContentPanel(ps));
33114         }
33115         else if(ps instanceof Array){
33116             for(var i =0, len = ps.length; i < len; i++){
33117                 this.addTypedPanels(lr, ps[i]);
33118             }
33119         }
33120         else if(!ps.events){ // raw config?
33121             var el = ps.el;
33122             delete ps.el; // prevent conflict
33123             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33124         }
33125         else {  // panel object assumed!
33126             lr.add(ps);
33127         }
33128     },
33129     /**
33130      * Adds a xtype elements to the layout.
33131      * <pre><code>
33132
33133 layout.addxtype({
33134        xtype : 'ContentPanel',
33135        region: 'west',
33136        items: [ .... ]
33137    }
33138 );
33139
33140 layout.addxtype({
33141         xtype : 'NestedLayoutPanel',
33142         region: 'west',
33143         layout: {
33144            center: { },
33145            west: { }   
33146         },
33147         items : [ ... list of content panels or nested layout panels.. ]
33148    }
33149 );
33150 </code></pre>
33151      * @param {Object} cfg Xtype definition of item to add.
33152      */
33153     addxtype : function(cfg)
33154     {
33155         // basically accepts a pannel...
33156         // can accept a layout region..!?!?
33157         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33158         
33159         if (!cfg.xtype.match(/Panel$/)) {
33160             return false;
33161         }
33162         var ret = false;
33163         
33164         if (typeof(cfg.region) == 'undefined') {
33165             Roo.log("Failed to add Panel, region was not set");
33166             Roo.log(cfg);
33167             return false;
33168         }
33169         var region = cfg.region;
33170         delete cfg.region;
33171         
33172           
33173         var xitems = [];
33174         if (cfg.items) {
33175             xitems = cfg.items;
33176             delete cfg.items;
33177         }
33178         var nb = false;
33179         
33180         switch(cfg.xtype) 
33181         {
33182             case 'ContentPanel':  // ContentPanel (el, cfg)
33183             case 'ScrollPanel':  // ContentPanel (el, cfg)
33184             case 'ViewPanel': 
33185                 if(cfg.autoCreate) {
33186                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33187                 } else {
33188                     var el = this.el.createChild();
33189                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33190                 }
33191                 
33192                 this.add(region, ret);
33193                 break;
33194             
33195             
33196             case 'TreePanel': // our new panel!
33197                 cfg.el = this.el.createChild();
33198                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33199                 this.add(region, ret);
33200                 break;
33201             
33202             case 'NestedLayoutPanel': 
33203                 // create a new Layout (which is  a Border Layout...
33204                 var el = this.el.createChild();
33205                 var clayout = cfg.layout;
33206                 delete cfg.layout;
33207                 clayout.items   = clayout.items  || [];
33208                 // replace this exitems with the clayout ones..
33209                 xitems = clayout.items;
33210                  
33211                 
33212                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33213                     cfg.background = false;
33214                 }
33215                 var layout = new Roo.BorderLayout(el, clayout);
33216                 
33217                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33218                 //console.log('adding nested layout panel '  + cfg.toSource());
33219                 this.add(region, ret);
33220                 nb = {}; /// find first...
33221                 break;
33222                 
33223             case 'GridPanel': 
33224             
33225                 // needs grid and region
33226                 
33227                 //var el = this.getRegion(region).el.createChild();
33228                 var el = this.el.createChild();
33229                 // create the grid first...
33230                 
33231                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33232                 delete cfg.grid;
33233                 if (region == 'center' && this.active ) {
33234                     cfg.background = false;
33235                 }
33236                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33237                 
33238                 this.add(region, ret);
33239                 if (cfg.background) {
33240                     ret.on('activate', function(gp) {
33241                         if (!gp.grid.rendered) {
33242                             gp.grid.render();
33243                         }
33244                     });
33245                 } else {
33246                     grid.render();
33247                 }
33248                 break;
33249            
33250            
33251            
33252                 
33253                 
33254                 
33255             default:
33256                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33257                     
33258                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33259                     this.add(region, ret);
33260                 } else {
33261                 
33262                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33263                     return null;
33264                 }
33265                 
33266              // GridPanel (grid, cfg)
33267             
33268         }
33269         this.beginUpdate();
33270         // add children..
33271         var region = '';
33272         var abn = {};
33273         Roo.each(xitems, function(i)  {
33274             region = nb && i.region ? i.region : false;
33275             
33276             var add = ret.addxtype(i);
33277            
33278             if (region) {
33279                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33280                 if (!i.background) {
33281                     abn[region] = nb[region] ;
33282                 }
33283             }
33284             
33285         });
33286         this.endUpdate();
33287
33288         // make the last non-background panel active..
33289         //if (nb) { Roo.log(abn); }
33290         if (nb) {
33291             
33292             for(var r in abn) {
33293                 region = this.getRegion(r);
33294                 if (region) {
33295                     // tried using nb[r], but it does not work..
33296                      
33297                     region.showPanel(abn[r]);
33298                    
33299                 }
33300             }
33301         }
33302         return ret;
33303         
33304     }
33305 });
33306
33307 /**
33308  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33309  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33310  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33311  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33312  * <pre><code>
33313 // shorthand
33314 var CP = Roo.ContentPanel;
33315
33316 var layout = Roo.BorderLayout.create({
33317     north: {
33318         initialSize: 25,
33319         titlebar: false,
33320         panels: [new CP("north", "North")]
33321     },
33322     west: {
33323         split:true,
33324         initialSize: 200,
33325         minSize: 175,
33326         maxSize: 400,
33327         titlebar: true,
33328         collapsible: true,
33329         panels: [new CP("west", {title: "West"})]
33330     },
33331     east: {
33332         split:true,
33333         initialSize: 202,
33334         minSize: 175,
33335         maxSize: 400,
33336         titlebar: true,
33337         collapsible: true,
33338         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33339     },
33340     south: {
33341         split:true,
33342         initialSize: 100,
33343         minSize: 100,
33344         maxSize: 200,
33345         titlebar: true,
33346         collapsible: true,
33347         panels: [new CP("south", {title: "South", closable: true})]
33348     },
33349     center: {
33350         titlebar: true,
33351         autoScroll:true,
33352         resizeTabs: true,
33353         minTabWidth: 50,
33354         preferredTabWidth: 150,
33355         panels: [
33356             new CP("center1", {title: "Close Me", closable: true}),
33357             new CP("center2", {title: "Center Panel", closable: false})
33358         ]
33359     }
33360 }, document.body);
33361
33362 layout.getRegion("center").showPanel("center1");
33363 </code></pre>
33364  * @param config
33365  * @param targetEl
33366  */
33367 Roo.BorderLayout.create = function(config, targetEl){
33368     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33369     layout.beginUpdate();
33370     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33371     for(var j = 0, jlen = regions.length; j < jlen; j++){
33372         var lr = regions[j];
33373         if(layout.regions[lr] && config[lr].panels){
33374             var r = layout.regions[lr];
33375             var ps = config[lr].panels;
33376             layout.addTypedPanels(r, ps);
33377         }
33378     }
33379     layout.endUpdate();
33380     return layout;
33381 };
33382
33383 // private
33384 Roo.BorderLayout.RegionFactory = {
33385     // private
33386     validRegions : ["north","south","east","west","center"],
33387
33388     // private
33389     create : function(target, mgr, config){
33390         target = target.toLowerCase();
33391         if(config.lightweight || config.basic){
33392             return new Roo.BasicLayoutRegion(mgr, config, target);
33393         }
33394         switch(target){
33395             case "north":
33396                 return new Roo.NorthLayoutRegion(mgr, config);
33397             case "south":
33398                 return new Roo.SouthLayoutRegion(mgr, config);
33399             case "east":
33400                 return new Roo.EastLayoutRegion(mgr, config);
33401             case "west":
33402                 return new Roo.WestLayoutRegion(mgr, config);
33403             case "center":
33404                 return new Roo.CenterLayoutRegion(mgr, config);
33405         }
33406         throw 'Layout region "'+target+'" not supported.';
33407     }
33408 };/*
33409  * Based on:
33410  * Ext JS Library 1.1.1
33411  * Copyright(c) 2006-2007, Ext JS, LLC.
33412  *
33413  * Originally Released Under LGPL - original licence link has changed is not relivant.
33414  *
33415  * Fork - LGPL
33416  * <script type="text/javascript">
33417  */
33418  
33419 /**
33420  * @class Roo.BasicLayoutRegion
33421  * @extends Roo.util.Observable
33422  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33423  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33424  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33425  */
33426 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33427     this.mgr = mgr;
33428     this.position  = pos;
33429     this.events = {
33430         /**
33431          * @scope Roo.BasicLayoutRegion
33432          */
33433         
33434         /**
33435          * @event beforeremove
33436          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33437          * @param {Roo.LayoutRegion} this
33438          * @param {Roo.ContentPanel} panel The panel
33439          * @param {Object} e The cancel event object
33440          */
33441         "beforeremove" : true,
33442         /**
33443          * @event invalidated
33444          * Fires when the layout for this region is changed.
33445          * @param {Roo.LayoutRegion} this
33446          */
33447         "invalidated" : true,
33448         /**
33449          * @event visibilitychange
33450          * Fires when this region is shown or hidden 
33451          * @param {Roo.LayoutRegion} this
33452          * @param {Boolean} visibility true or false
33453          */
33454         "visibilitychange" : true,
33455         /**
33456          * @event paneladded
33457          * Fires when a panel is added. 
33458          * @param {Roo.LayoutRegion} this
33459          * @param {Roo.ContentPanel} panel The panel
33460          */
33461         "paneladded" : true,
33462         /**
33463          * @event panelremoved
33464          * Fires when a panel is removed. 
33465          * @param {Roo.LayoutRegion} this
33466          * @param {Roo.ContentPanel} panel The panel
33467          */
33468         "panelremoved" : true,
33469         /**
33470          * @event collapsed
33471          * Fires when this region is collapsed.
33472          * @param {Roo.LayoutRegion} this
33473          */
33474         "collapsed" : true,
33475         /**
33476          * @event expanded
33477          * Fires when this region is expanded.
33478          * @param {Roo.LayoutRegion} this
33479          */
33480         "expanded" : true,
33481         /**
33482          * @event slideshow
33483          * Fires when this region is slid into view.
33484          * @param {Roo.LayoutRegion} this
33485          */
33486         "slideshow" : true,
33487         /**
33488          * @event slidehide
33489          * Fires when this region slides out of view. 
33490          * @param {Roo.LayoutRegion} this
33491          */
33492         "slidehide" : true,
33493         /**
33494          * @event panelactivated
33495          * Fires when a panel is activated. 
33496          * @param {Roo.LayoutRegion} this
33497          * @param {Roo.ContentPanel} panel The activated panel
33498          */
33499         "panelactivated" : true,
33500         /**
33501          * @event resized
33502          * Fires when the user resizes this region. 
33503          * @param {Roo.LayoutRegion} this
33504          * @param {Number} newSize The new size (width for east/west, height for north/south)
33505          */
33506         "resized" : true
33507     };
33508     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33509     this.panels = new Roo.util.MixedCollection();
33510     this.panels.getKey = this.getPanelId.createDelegate(this);
33511     this.box = null;
33512     this.activePanel = null;
33513     // ensure listeners are added...
33514     
33515     if (config.listeners || config.events) {
33516         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33517             listeners : config.listeners || {},
33518             events : config.events || {}
33519         });
33520     }
33521     
33522     if(skipConfig !== true){
33523         this.applyConfig(config);
33524     }
33525 };
33526
33527 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33528     getPanelId : function(p){
33529         return p.getId();
33530     },
33531     
33532     applyConfig : function(config){
33533         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33534         this.config = config;
33535         
33536     },
33537     
33538     /**
33539      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33540      * the width, for horizontal (north, south) the height.
33541      * @param {Number} newSize The new width or height
33542      */
33543     resizeTo : function(newSize){
33544         var el = this.el ? this.el :
33545                  (this.activePanel ? this.activePanel.getEl() : null);
33546         if(el){
33547             switch(this.position){
33548                 case "east":
33549                 case "west":
33550                     el.setWidth(newSize);
33551                     this.fireEvent("resized", this, newSize);
33552                 break;
33553                 case "north":
33554                 case "south":
33555                     el.setHeight(newSize);
33556                     this.fireEvent("resized", this, newSize);
33557                 break;                
33558             }
33559         }
33560     },
33561     
33562     getBox : function(){
33563         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33564     },
33565     
33566     getMargins : function(){
33567         return this.margins;
33568     },
33569     
33570     updateBox : function(box){
33571         this.box = box;
33572         var el = this.activePanel.getEl();
33573         el.dom.style.left = box.x + "px";
33574         el.dom.style.top = box.y + "px";
33575         this.activePanel.setSize(box.width, box.height);
33576     },
33577     
33578     /**
33579      * Returns the container element for this region.
33580      * @return {Roo.Element}
33581      */
33582     getEl : function(){
33583         return this.activePanel;
33584     },
33585     
33586     /**
33587      * Returns true if this region is currently visible.
33588      * @return {Boolean}
33589      */
33590     isVisible : function(){
33591         return this.activePanel ? true : false;
33592     },
33593     
33594     setActivePanel : function(panel){
33595         panel = this.getPanel(panel);
33596         if(this.activePanel && this.activePanel != panel){
33597             this.activePanel.setActiveState(false);
33598             this.activePanel.getEl().setLeftTop(-10000,-10000);
33599         }
33600         this.activePanel = panel;
33601         panel.setActiveState(true);
33602         if(this.box){
33603             panel.setSize(this.box.width, this.box.height);
33604         }
33605         this.fireEvent("panelactivated", this, panel);
33606         this.fireEvent("invalidated");
33607     },
33608     
33609     /**
33610      * Show the specified panel.
33611      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33612      * @return {Roo.ContentPanel} The shown panel or null
33613      */
33614     showPanel : function(panel){
33615         if(panel = this.getPanel(panel)){
33616             this.setActivePanel(panel);
33617         }
33618         return panel;
33619     },
33620     
33621     /**
33622      * Get the active panel for this region.
33623      * @return {Roo.ContentPanel} The active panel or null
33624      */
33625     getActivePanel : function(){
33626         return this.activePanel;
33627     },
33628     
33629     /**
33630      * Add the passed ContentPanel(s)
33631      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33632      * @return {Roo.ContentPanel} The panel added (if only one was added)
33633      */
33634     add : function(panel){
33635         if(arguments.length > 1){
33636             for(var i = 0, len = arguments.length; i < len; i++) {
33637                 this.add(arguments[i]);
33638             }
33639             return null;
33640         }
33641         if(this.hasPanel(panel)){
33642             this.showPanel(panel);
33643             return panel;
33644         }
33645         var el = panel.getEl();
33646         if(el.dom.parentNode != this.mgr.el.dom){
33647             this.mgr.el.dom.appendChild(el.dom);
33648         }
33649         if(panel.setRegion){
33650             panel.setRegion(this);
33651         }
33652         this.panels.add(panel);
33653         el.setStyle("position", "absolute");
33654         if(!panel.background){
33655             this.setActivePanel(panel);
33656             if(this.config.initialSize && this.panels.getCount()==1){
33657                 this.resizeTo(this.config.initialSize);
33658             }
33659         }
33660         this.fireEvent("paneladded", this, panel);
33661         return panel;
33662     },
33663     
33664     /**
33665      * Returns true if the panel is in this region.
33666      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33667      * @return {Boolean}
33668      */
33669     hasPanel : function(panel){
33670         if(typeof panel == "object"){ // must be panel obj
33671             panel = panel.getId();
33672         }
33673         return this.getPanel(panel) ? true : false;
33674     },
33675     
33676     /**
33677      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33678      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33679      * @param {Boolean} preservePanel Overrides the config preservePanel option
33680      * @return {Roo.ContentPanel} The panel that was removed
33681      */
33682     remove : function(panel, preservePanel){
33683         panel = this.getPanel(panel);
33684         if(!panel){
33685             return null;
33686         }
33687         var e = {};
33688         this.fireEvent("beforeremove", this, panel, e);
33689         if(e.cancel === true){
33690             return null;
33691         }
33692         var panelId = panel.getId();
33693         this.panels.removeKey(panelId);
33694         return panel;
33695     },
33696     
33697     /**
33698      * Returns the panel specified or null if it's not in this region.
33699      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33700      * @return {Roo.ContentPanel}
33701      */
33702     getPanel : function(id){
33703         if(typeof id == "object"){ // must be panel obj
33704             return id;
33705         }
33706         return this.panels.get(id);
33707     },
33708     
33709     /**
33710      * Returns this regions position (north/south/east/west/center).
33711      * @return {String} 
33712      */
33713     getPosition: function(){
33714         return this.position;    
33715     }
33716 });/*
33717  * Based on:
33718  * Ext JS Library 1.1.1
33719  * Copyright(c) 2006-2007, Ext JS, LLC.
33720  *
33721  * Originally Released Under LGPL - original licence link has changed is not relivant.
33722  *
33723  * Fork - LGPL
33724  * <script type="text/javascript">
33725  */
33726  
33727 /**
33728  * @class Roo.LayoutRegion
33729  * @extends Roo.BasicLayoutRegion
33730  * This class represents a region in a layout manager.
33731  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33732  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33733  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33734  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33735  * @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})
33736  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33737  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33738  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33739  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33740  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33741  * @cfg {String}    title           The title for the region (overrides panel titles)
33742  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33743  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33744  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33745  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33746  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33747  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33748  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33749  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33750  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33751  * @cfg {Boolean}   showPin         True to show a pin button
33752  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33753  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33754  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33755  * @cfg {Number}    width           For East/West panels
33756  * @cfg {Number}    height          For North/South panels
33757  * @cfg {Boolean}   split           To show the splitter
33758  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33759  */
33760 Roo.LayoutRegion = function(mgr, config, pos){
33761     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33762     var dh = Roo.DomHelper;
33763     /** This region's container element 
33764     * @type Roo.Element */
33765     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33766     /** This region's title element 
33767     * @type Roo.Element */
33768
33769     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33770         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33771         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33772     ]}, true);
33773     this.titleEl.enableDisplayMode();
33774     /** This region's title text element 
33775     * @type HTMLElement */
33776     this.titleTextEl = this.titleEl.dom.firstChild;
33777     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33778     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33779     this.closeBtn.enableDisplayMode();
33780     this.closeBtn.on("click", this.closeClicked, this);
33781     this.closeBtn.hide();
33782
33783     this.createBody(config);
33784     this.visible = true;
33785     this.collapsed = false;
33786
33787     if(config.hideWhenEmpty){
33788         this.hide();
33789         this.on("paneladded", this.validateVisibility, this);
33790         this.on("panelremoved", this.validateVisibility, this);
33791     }
33792     this.applyConfig(config);
33793 };
33794
33795 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33796
33797     createBody : function(){
33798         /** This region's body element 
33799         * @type Roo.Element */
33800         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33801     },
33802
33803     applyConfig : function(c){
33804         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33805             var dh = Roo.DomHelper;
33806             if(c.titlebar !== false){
33807                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33808                 this.collapseBtn.on("click", this.collapse, this);
33809                 this.collapseBtn.enableDisplayMode();
33810
33811                 if(c.showPin === true || this.showPin){
33812                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33813                     this.stickBtn.enableDisplayMode();
33814                     this.stickBtn.on("click", this.expand, this);
33815                     this.stickBtn.hide();
33816                 }
33817             }
33818             /** This region's collapsed element
33819             * @type Roo.Element */
33820             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33821                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33822             ]}, true);
33823             if(c.floatable !== false){
33824                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33825                this.collapsedEl.on("click", this.collapseClick, this);
33826             }
33827
33828             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33829                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33830                    id: "message", unselectable: "on", style:{"float":"left"}});
33831                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33832              }
33833             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33834             this.expandBtn.on("click", this.expand, this);
33835         }
33836         if(this.collapseBtn){
33837             this.collapseBtn.setVisible(c.collapsible == true);
33838         }
33839         this.cmargins = c.cmargins || this.cmargins ||
33840                          (this.position == "west" || this.position == "east" ?
33841                              {top: 0, left: 2, right:2, bottom: 0} :
33842                              {top: 2, left: 0, right:0, bottom: 2});
33843         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33844         this.bottomTabs = c.tabPosition != "top";
33845         this.autoScroll = c.autoScroll || false;
33846         if(this.autoScroll){
33847             this.bodyEl.setStyle("overflow", "auto");
33848         }else{
33849             this.bodyEl.setStyle("overflow", "hidden");
33850         }
33851         //if(c.titlebar !== false){
33852             if((!c.titlebar && !c.title) || c.titlebar === false){
33853                 this.titleEl.hide();
33854             }else{
33855                 this.titleEl.show();
33856                 if(c.title){
33857                     this.titleTextEl.innerHTML = c.title;
33858                 }
33859             }
33860         //}
33861         this.duration = c.duration || .30;
33862         this.slideDuration = c.slideDuration || .45;
33863         this.config = c;
33864         if(c.collapsed){
33865             this.collapse(true);
33866         }
33867         if(c.hidden){
33868             this.hide();
33869         }
33870     },
33871     /**
33872      * Returns true if this region is currently visible.
33873      * @return {Boolean}
33874      */
33875     isVisible : function(){
33876         return this.visible;
33877     },
33878
33879     /**
33880      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33881      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33882      */
33883     setCollapsedTitle : function(title){
33884         title = title || "&#160;";
33885         if(this.collapsedTitleTextEl){
33886             this.collapsedTitleTextEl.innerHTML = title;
33887         }
33888     },
33889
33890     getBox : function(){
33891         var b;
33892         if(!this.collapsed){
33893             b = this.el.getBox(false, true);
33894         }else{
33895             b = this.collapsedEl.getBox(false, true);
33896         }
33897         return b;
33898     },
33899
33900     getMargins : function(){
33901         return this.collapsed ? this.cmargins : this.margins;
33902     },
33903
33904     highlight : function(){
33905         this.el.addClass("x-layout-panel-dragover");
33906     },
33907
33908     unhighlight : function(){
33909         this.el.removeClass("x-layout-panel-dragover");
33910     },
33911
33912     updateBox : function(box){
33913         this.box = box;
33914         if(!this.collapsed){
33915             this.el.dom.style.left = box.x + "px";
33916             this.el.dom.style.top = box.y + "px";
33917             this.updateBody(box.width, box.height);
33918         }else{
33919             this.collapsedEl.dom.style.left = box.x + "px";
33920             this.collapsedEl.dom.style.top = box.y + "px";
33921             this.collapsedEl.setSize(box.width, box.height);
33922         }
33923         if(this.tabs){
33924             this.tabs.autoSizeTabs();
33925         }
33926     },
33927
33928     updateBody : function(w, h){
33929         if(w !== null){
33930             this.el.setWidth(w);
33931             w -= this.el.getBorderWidth("rl");
33932             if(this.config.adjustments){
33933                 w += this.config.adjustments[0];
33934             }
33935         }
33936         if(h !== null){
33937             this.el.setHeight(h);
33938             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33939             h -= this.el.getBorderWidth("tb");
33940             if(this.config.adjustments){
33941                 h += this.config.adjustments[1];
33942             }
33943             this.bodyEl.setHeight(h);
33944             if(this.tabs){
33945                 h = this.tabs.syncHeight(h);
33946             }
33947         }
33948         if(this.panelSize){
33949             w = w !== null ? w : this.panelSize.width;
33950             h = h !== null ? h : this.panelSize.height;
33951         }
33952         if(this.activePanel){
33953             var el = this.activePanel.getEl();
33954             w = w !== null ? w : el.getWidth();
33955             h = h !== null ? h : el.getHeight();
33956             this.panelSize = {width: w, height: h};
33957             this.activePanel.setSize(w, h);
33958         }
33959         if(Roo.isIE && this.tabs){
33960             this.tabs.el.repaint();
33961         }
33962     },
33963
33964     /**
33965      * Returns the container element for this region.
33966      * @return {Roo.Element}
33967      */
33968     getEl : function(){
33969         return this.el;
33970     },
33971
33972     /**
33973      * Hides this region.
33974      */
33975     hide : function(){
33976         if(!this.collapsed){
33977             this.el.dom.style.left = "-2000px";
33978             this.el.hide();
33979         }else{
33980             this.collapsedEl.dom.style.left = "-2000px";
33981             this.collapsedEl.hide();
33982         }
33983         this.visible = false;
33984         this.fireEvent("visibilitychange", this, false);
33985     },
33986
33987     /**
33988      * Shows this region if it was previously hidden.
33989      */
33990     show : function(){
33991         if(!this.collapsed){
33992             this.el.show();
33993         }else{
33994             this.collapsedEl.show();
33995         }
33996         this.visible = true;
33997         this.fireEvent("visibilitychange", this, true);
33998     },
33999
34000     closeClicked : function(){
34001         if(this.activePanel){
34002             this.remove(this.activePanel);
34003         }
34004     },
34005
34006     collapseClick : function(e){
34007         if(this.isSlid){
34008            e.stopPropagation();
34009            this.slideIn();
34010         }else{
34011            e.stopPropagation();
34012            this.slideOut();
34013         }
34014     },
34015
34016     /**
34017      * Collapses this region.
34018      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34019      */
34020     collapse : function(skipAnim){
34021         if(this.collapsed) return;
34022         this.collapsed = true;
34023         if(this.split){
34024             this.split.el.hide();
34025         }
34026         if(this.config.animate && skipAnim !== true){
34027             this.fireEvent("invalidated", this);
34028             this.animateCollapse();
34029         }else{
34030             this.el.setLocation(-20000,-20000);
34031             this.el.hide();
34032             this.collapsedEl.show();
34033             this.fireEvent("collapsed", this);
34034             this.fireEvent("invalidated", this);
34035         }
34036     },
34037
34038     animateCollapse : function(){
34039         // overridden
34040     },
34041
34042     /**
34043      * Expands this region if it was previously collapsed.
34044      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34045      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34046      */
34047     expand : function(e, skipAnim){
34048         if(e) e.stopPropagation();
34049         if(!this.collapsed || this.el.hasActiveFx()) return;
34050         if(this.isSlid){
34051             this.afterSlideIn();
34052             skipAnim = true;
34053         }
34054         this.collapsed = false;
34055         if(this.config.animate && skipAnim !== true){
34056             this.animateExpand();
34057         }else{
34058             this.el.show();
34059             if(this.split){
34060                 this.split.el.show();
34061             }
34062             this.collapsedEl.setLocation(-2000,-2000);
34063             this.collapsedEl.hide();
34064             this.fireEvent("invalidated", this);
34065             this.fireEvent("expanded", this);
34066         }
34067     },
34068
34069     animateExpand : function(){
34070         // overridden
34071     },
34072
34073     initTabs : function()
34074     {
34075         this.bodyEl.setStyle("overflow", "hidden");
34076         var ts = new Roo.TabPanel(
34077                 this.bodyEl.dom,
34078                 {
34079                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34080                     disableTooltips: this.config.disableTabTips,
34081                     toolbar : this.config.toolbar
34082                 }
34083         );
34084         if(this.config.hideTabs){
34085             ts.stripWrap.setDisplayed(false);
34086         }
34087         this.tabs = ts;
34088         ts.resizeTabs = this.config.resizeTabs === true;
34089         ts.minTabWidth = this.config.minTabWidth || 40;
34090         ts.maxTabWidth = this.config.maxTabWidth || 250;
34091         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34092         ts.monitorResize = false;
34093         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34094         ts.bodyEl.addClass('x-layout-tabs-body');
34095         this.panels.each(this.initPanelAsTab, this);
34096     },
34097
34098     initPanelAsTab : function(panel){
34099         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34100                     this.config.closeOnTab && panel.isClosable());
34101         if(panel.tabTip !== undefined){
34102             ti.setTooltip(panel.tabTip);
34103         }
34104         ti.on("activate", function(){
34105               this.setActivePanel(panel);
34106         }, this);
34107         if(this.config.closeOnTab){
34108             ti.on("beforeclose", function(t, e){
34109                 e.cancel = true;
34110                 this.remove(panel);
34111             }, this);
34112         }
34113         return ti;
34114     },
34115
34116     updatePanelTitle : function(panel, title){
34117         if(this.activePanel == panel){
34118             this.updateTitle(title);
34119         }
34120         if(this.tabs){
34121             var ti = this.tabs.getTab(panel.getEl().id);
34122             ti.setText(title);
34123             if(panel.tabTip !== undefined){
34124                 ti.setTooltip(panel.tabTip);
34125             }
34126         }
34127     },
34128
34129     updateTitle : function(title){
34130         if(this.titleTextEl && !this.config.title){
34131             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34132         }
34133     },
34134
34135     setActivePanel : function(panel){
34136         panel = this.getPanel(panel);
34137         if(this.activePanel && this.activePanel != panel){
34138             this.activePanel.setActiveState(false);
34139         }
34140         this.activePanel = panel;
34141         panel.setActiveState(true);
34142         if(this.panelSize){
34143             panel.setSize(this.panelSize.width, this.panelSize.height);
34144         }
34145         if(this.closeBtn){
34146             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34147         }
34148         this.updateTitle(panel.getTitle());
34149         if(this.tabs){
34150             this.fireEvent("invalidated", this);
34151         }
34152         this.fireEvent("panelactivated", this, panel);
34153     },
34154
34155     /**
34156      * Shows the specified panel.
34157      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34158      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34159      */
34160     showPanel : function(panel)
34161     {
34162         panel = this.getPanel(panel);
34163         if(panel){
34164             if(this.tabs){
34165                 var tab = this.tabs.getTab(panel.getEl().id);
34166                 if(tab.isHidden()){
34167                     this.tabs.unhideTab(tab.id);
34168                 }
34169                 tab.activate();
34170             }else{
34171                 this.setActivePanel(panel);
34172             }
34173         }
34174         return panel;
34175     },
34176
34177     /**
34178      * Get the active panel for this region.
34179      * @return {Roo.ContentPanel} The active panel or null
34180      */
34181     getActivePanel : function(){
34182         return this.activePanel;
34183     },
34184
34185     validateVisibility : function(){
34186         if(this.panels.getCount() < 1){
34187             this.updateTitle("&#160;");
34188             this.closeBtn.hide();
34189             this.hide();
34190         }else{
34191             if(!this.isVisible()){
34192                 this.show();
34193             }
34194         }
34195     },
34196
34197     /**
34198      * Adds the passed ContentPanel(s) to this region.
34199      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34200      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34201      */
34202     add : function(panel){
34203         if(arguments.length > 1){
34204             for(var i = 0, len = arguments.length; i < len; i++) {
34205                 this.add(arguments[i]);
34206             }
34207             return null;
34208         }
34209         if(this.hasPanel(panel)){
34210             this.showPanel(panel);
34211             return panel;
34212         }
34213         panel.setRegion(this);
34214         this.panels.add(panel);
34215         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34216             this.bodyEl.dom.appendChild(panel.getEl().dom);
34217             if(panel.background !== true){
34218                 this.setActivePanel(panel);
34219             }
34220             this.fireEvent("paneladded", this, panel);
34221             return panel;
34222         }
34223         if(!this.tabs){
34224             this.initTabs();
34225         }else{
34226             this.initPanelAsTab(panel);
34227         }
34228         if(panel.background !== true){
34229             this.tabs.activate(panel.getEl().id);
34230         }
34231         this.fireEvent("paneladded", this, panel);
34232         return panel;
34233     },
34234
34235     /**
34236      * Hides the tab for the specified panel.
34237      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34238      */
34239     hidePanel : function(panel){
34240         if(this.tabs && (panel = this.getPanel(panel))){
34241             this.tabs.hideTab(panel.getEl().id);
34242         }
34243     },
34244
34245     /**
34246      * Unhides the tab for a previously hidden panel.
34247      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34248      */
34249     unhidePanel : function(panel){
34250         if(this.tabs && (panel = this.getPanel(panel))){
34251             this.tabs.unhideTab(panel.getEl().id);
34252         }
34253     },
34254
34255     clearPanels : function(){
34256         while(this.panels.getCount() > 0){
34257              this.remove(this.panels.first());
34258         }
34259     },
34260
34261     /**
34262      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34263      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34264      * @param {Boolean} preservePanel Overrides the config preservePanel option
34265      * @return {Roo.ContentPanel} The panel that was removed
34266      */
34267     remove : function(panel, preservePanel){
34268         panel = this.getPanel(panel);
34269         if(!panel){
34270             return null;
34271         }
34272         var e = {};
34273         this.fireEvent("beforeremove", this, panel, e);
34274         if(e.cancel === true){
34275             return null;
34276         }
34277         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34278         var panelId = panel.getId();
34279         this.panels.removeKey(panelId);
34280         if(preservePanel){
34281             document.body.appendChild(panel.getEl().dom);
34282         }
34283         if(this.tabs){
34284             this.tabs.removeTab(panel.getEl().id);
34285         }else if (!preservePanel){
34286             this.bodyEl.dom.removeChild(panel.getEl().dom);
34287         }
34288         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34289             var p = this.panels.first();
34290             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34291             tempEl.appendChild(p.getEl().dom);
34292             this.bodyEl.update("");
34293             this.bodyEl.dom.appendChild(p.getEl().dom);
34294             tempEl = null;
34295             this.updateTitle(p.getTitle());
34296             this.tabs = null;
34297             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34298             this.setActivePanel(p);
34299         }
34300         panel.setRegion(null);
34301         if(this.activePanel == panel){
34302             this.activePanel = null;
34303         }
34304         if(this.config.autoDestroy !== false && preservePanel !== true){
34305             try{panel.destroy();}catch(e){}
34306         }
34307         this.fireEvent("panelremoved", this, panel);
34308         return panel;
34309     },
34310
34311     /**
34312      * Returns the TabPanel component used by this region
34313      * @return {Roo.TabPanel}
34314      */
34315     getTabs : function(){
34316         return this.tabs;
34317     },
34318
34319     createTool : function(parentEl, className){
34320         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34321             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34322         btn.addClassOnOver("x-layout-tools-button-over");
34323         return btn;
34324     }
34325 });/*
34326  * Based on:
34327  * Ext JS Library 1.1.1
34328  * Copyright(c) 2006-2007, Ext JS, LLC.
34329  *
34330  * Originally Released Under LGPL - original licence link has changed is not relivant.
34331  *
34332  * Fork - LGPL
34333  * <script type="text/javascript">
34334  */
34335  
34336
34337
34338 /**
34339  * @class Roo.SplitLayoutRegion
34340  * @extends Roo.LayoutRegion
34341  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34342  */
34343 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34344     this.cursor = cursor;
34345     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34346 };
34347
34348 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34349     splitTip : "Drag to resize.",
34350     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34351     useSplitTips : false,
34352
34353     applyConfig : function(config){
34354         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34355         if(config.split){
34356             if(!this.split){
34357                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34358                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34359                 /** The SplitBar for this region 
34360                 * @type Roo.SplitBar */
34361                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34362                 this.split.on("moved", this.onSplitMove, this);
34363                 this.split.useShim = config.useShim === true;
34364                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34365                 if(this.useSplitTips){
34366                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34367                 }
34368                 if(config.collapsible){
34369                     this.split.el.on("dblclick", this.collapse,  this);
34370                 }
34371             }
34372             if(typeof config.minSize != "undefined"){
34373                 this.split.minSize = config.minSize;
34374             }
34375             if(typeof config.maxSize != "undefined"){
34376                 this.split.maxSize = config.maxSize;
34377             }
34378             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34379                 this.hideSplitter();
34380             }
34381         }
34382     },
34383
34384     getHMaxSize : function(){
34385          var cmax = this.config.maxSize || 10000;
34386          var center = this.mgr.getRegion("center");
34387          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34388     },
34389
34390     getVMaxSize : function(){
34391          var cmax = this.config.maxSize || 10000;
34392          var center = this.mgr.getRegion("center");
34393          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34394     },
34395
34396     onSplitMove : function(split, newSize){
34397         this.fireEvent("resized", this, newSize);
34398     },
34399     
34400     /** 
34401      * Returns the {@link Roo.SplitBar} for this region.
34402      * @return {Roo.SplitBar}
34403      */
34404     getSplitBar : function(){
34405         return this.split;
34406     },
34407     
34408     hide : function(){
34409         this.hideSplitter();
34410         Roo.SplitLayoutRegion.superclass.hide.call(this);
34411     },
34412
34413     hideSplitter : function(){
34414         if(this.split){
34415             this.split.el.setLocation(-2000,-2000);
34416             this.split.el.hide();
34417         }
34418     },
34419
34420     show : function(){
34421         if(this.split){
34422             this.split.el.show();
34423         }
34424         Roo.SplitLayoutRegion.superclass.show.call(this);
34425     },
34426     
34427     beforeSlide: function(){
34428         if(Roo.isGecko){// firefox overflow auto bug workaround
34429             this.bodyEl.clip();
34430             if(this.tabs) this.tabs.bodyEl.clip();
34431             if(this.activePanel){
34432                 this.activePanel.getEl().clip();
34433                 
34434                 if(this.activePanel.beforeSlide){
34435                     this.activePanel.beforeSlide();
34436                 }
34437             }
34438         }
34439     },
34440     
34441     afterSlide : function(){
34442         if(Roo.isGecko){// firefox overflow auto bug workaround
34443             this.bodyEl.unclip();
34444             if(this.tabs) this.tabs.bodyEl.unclip();
34445             if(this.activePanel){
34446                 this.activePanel.getEl().unclip();
34447                 if(this.activePanel.afterSlide){
34448                     this.activePanel.afterSlide();
34449                 }
34450             }
34451         }
34452     },
34453
34454     initAutoHide : function(){
34455         if(this.autoHide !== false){
34456             if(!this.autoHideHd){
34457                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34458                 this.autoHideHd = {
34459                     "mouseout": function(e){
34460                         if(!e.within(this.el, true)){
34461                             st.delay(500);
34462                         }
34463                     },
34464                     "mouseover" : function(e){
34465                         st.cancel();
34466                     },
34467                     scope : this
34468                 };
34469             }
34470             this.el.on(this.autoHideHd);
34471         }
34472     },
34473
34474     clearAutoHide : function(){
34475         if(this.autoHide !== false){
34476             this.el.un("mouseout", this.autoHideHd.mouseout);
34477             this.el.un("mouseover", this.autoHideHd.mouseover);
34478         }
34479     },
34480
34481     clearMonitor : function(){
34482         Roo.get(document).un("click", this.slideInIf, this);
34483     },
34484
34485     // these names are backwards but not changed for compat
34486     slideOut : function(){
34487         if(this.isSlid || this.el.hasActiveFx()){
34488             return;
34489         }
34490         this.isSlid = true;
34491         if(this.collapseBtn){
34492             this.collapseBtn.hide();
34493         }
34494         this.closeBtnState = this.closeBtn.getStyle('display');
34495         this.closeBtn.hide();
34496         if(this.stickBtn){
34497             this.stickBtn.show();
34498         }
34499         this.el.show();
34500         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34501         this.beforeSlide();
34502         this.el.setStyle("z-index", 10001);
34503         this.el.slideIn(this.getSlideAnchor(), {
34504             callback: function(){
34505                 this.afterSlide();
34506                 this.initAutoHide();
34507                 Roo.get(document).on("click", this.slideInIf, this);
34508                 this.fireEvent("slideshow", this);
34509             },
34510             scope: this,
34511             block: true
34512         });
34513     },
34514
34515     afterSlideIn : function(){
34516         this.clearAutoHide();
34517         this.isSlid = false;
34518         this.clearMonitor();
34519         this.el.setStyle("z-index", "");
34520         if(this.collapseBtn){
34521             this.collapseBtn.show();
34522         }
34523         this.closeBtn.setStyle('display', this.closeBtnState);
34524         if(this.stickBtn){
34525             this.stickBtn.hide();
34526         }
34527         this.fireEvent("slidehide", this);
34528     },
34529
34530     slideIn : function(cb){
34531         if(!this.isSlid || this.el.hasActiveFx()){
34532             Roo.callback(cb);
34533             return;
34534         }
34535         this.isSlid = false;
34536         this.beforeSlide();
34537         this.el.slideOut(this.getSlideAnchor(), {
34538             callback: function(){
34539                 this.el.setLeftTop(-10000, -10000);
34540                 this.afterSlide();
34541                 this.afterSlideIn();
34542                 Roo.callback(cb);
34543             },
34544             scope: this,
34545             block: true
34546         });
34547     },
34548     
34549     slideInIf : function(e){
34550         if(!e.within(this.el)){
34551             this.slideIn();
34552         }
34553     },
34554
34555     animateCollapse : function(){
34556         this.beforeSlide();
34557         this.el.setStyle("z-index", 20000);
34558         var anchor = this.getSlideAnchor();
34559         this.el.slideOut(anchor, {
34560             callback : function(){
34561                 this.el.setStyle("z-index", "");
34562                 this.collapsedEl.slideIn(anchor, {duration:.3});
34563                 this.afterSlide();
34564                 this.el.setLocation(-10000,-10000);
34565                 this.el.hide();
34566                 this.fireEvent("collapsed", this);
34567             },
34568             scope: this,
34569             block: true
34570         });
34571     },
34572
34573     animateExpand : function(){
34574         this.beforeSlide();
34575         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34576         this.el.setStyle("z-index", 20000);
34577         this.collapsedEl.hide({
34578             duration:.1
34579         });
34580         this.el.slideIn(this.getSlideAnchor(), {
34581             callback : function(){
34582                 this.el.setStyle("z-index", "");
34583                 this.afterSlide();
34584                 if(this.split){
34585                     this.split.el.show();
34586                 }
34587                 this.fireEvent("invalidated", this);
34588                 this.fireEvent("expanded", this);
34589             },
34590             scope: this,
34591             block: true
34592         });
34593     },
34594
34595     anchors : {
34596         "west" : "left",
34597         "east" : "right",
34598         "north" : "top",
34599         "south" : "bottom"
34600     },
34601
34602     sanchors : {
34603         "west" : "l",
34604         "east" : "r",
34605         "north" : "t",
34606         "south" : "b"
34607     },
34608
34609     canchors : {
34610         "west" : "tl-tr",
34611         "east" : "tr-tl",
34612         "north" : "tl-bl",
34613         "south" : "bl-tl"
34614     },
34615
34616     getAnchor : function(){
34617         return this.anchors[this.position];
34618     },
34619
34620     getCollapseAnchor : function(){
34621         return this.canchors[this.position];
34622     },
34623
34624     getSlideAnchor : function(){
34625         return this.sanchors[this.position];
34626     },
34627
34628     getAlignAdj : function(){
34629         var cm = this.cmargins;
34630         switch(this.position){
34631             case "west":
34632                 return [0, 0];
34633             break;
34634             case "east":
34635                 return [0, 0];
34636             break;
34637             case "north":
34638                 return [0, 0];
34639             break;
34640             case "south":
34641                 return [0, 0];
34642             break;
34643         }
34644     },
34645
34646     getExpandAdj : function(){
34647         var c = this.collapsedEl, cm = this.cmargins;
34648         switch(this.position){
34649             case "west":
34650                 return [-(cm.right+c.getWidth()+cm.left), 0];
34651             break;
34652             case "east":
34653                 return [cm.right+c.getWidth()+cm.left, 0];
34654             break;
34655             case "north":
34656                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34657             break;
34658             case "south":
34659                 return [0, cm.top+cm.bottom+c.getHeight()];
34660             break;
34661         }
34662     }
34663 });/*
34664  * Based on:
34665  * Ext JS Library 1.1.1
34666  * Copyright(c) 2006-2007, Ext JS, LLC.
34667  *
34668  * Originally Released Under LGPL - original licence link has changed is not relivant.
34669  *
34670  * Fork - LGPL
34671  * <script type="text/javascript">
34672  */
34673 /*
34674  * These classes are private internal classes
34675  */
34676 Roo.CenterLayoutRegion = function(mgr, config){
34677     Roo.LayoutRegion.call(this, mgr, config, "center");
34678     this.visible = true;
34679     this.minWidth = config.minWidth || 20;
34680     this.minHeight = config.minHeight || 20;
34681 };
34682
34683 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34684     hide : function(){
34685         // center panel can't be hidden
34686     },
34687     
34688     show : function(){
34689         // center panel can't be hidden
34690     },
34691     
34692     getMinWidth: function(){
34693         return this.minWidth;
34694     },
34695     
34696     getMinHeight: function(){
34697         return this.minHeight;
34698     }
34699 });
34700
34701
34702 Roo.NorthLayoutRegion = function(mgr, config){
34703     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34704     if(this.split){
34705         this.split.placement = Roo.SplitBar.TOP;
34706         this.split.orientation = Roo.SplitBar.VERTICAL;
34707         this.split.el.addClass("x-layout-split-v");
34708     }
34709     var size = config.initialSize || config.height;
34710     if(typeof size != "undefined"){
34711         this.el.setHeight(size);
34712     }
34713 };
34714 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34715     orientation: Roo.SplitBar.VERTICAL,
34716     getBox : function(){
34717         if(this.collapsed){
34718             return this.collapsedEl.getBox();
34719         }
34720         var box = this.el.getBox();
34721         if(this.split){
34722             box.height += this.split.el.getHeight();
34723         }
34724         return box;
34725     },
34726     
34727     updateBox : function(box){
34728         if(this.split && !this.collapsed){
34729             box.height -= this.split.el.getHeight();
34730             this.split.el.setLeft(box.x);
34731             this.split.el.setTop(box.y+box.height);
34732             this.split.el.setWidth(box.width);
34733         }
34734         if(this.collapsed){
34735             this.updateBody(box.width, null);
34736         }
34737         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34738     }
34739 });
34740
34741 Roo.SouthLayoutRegion = function(mgr, config){
34742     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34743     if(this.split){
34744         this.split.placement = Roo.SplitBar.BOTTOM;
34745         this.split.orientation = Roo.SplitBar.VERTICAL;
34746         this.split.el.addClass("x-layout-split-v");
34747     }
34748     var size = config.initialSize || config.height;
34749     if(typeof size != "undefined"){
34750         this.el.setHeight(size);
34751     }
34752 };
34753 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34754     orientation: Roo.SplitBar.VERTICAL,
34755     getBox : function(){
34756         if(this.collapsed){
34757             return this.collapsedEl.getBox();
34758         }
34759         var box = this.el.getBox();
34760         if(this.split){
34761             var sh = this.split.el.getHeight();
34762             box.height += sh;
34763             box.y -= sh;
34764         }
34765         return box;
34766     },
34767     
34768     updateBox : function(box){
34769         if(this.split && !this.collapsed){
34770             var sh = this.split.el.getHeight();
34771             box.height -= sh;
34772             box.y += sh;
34773             this.split.el.setLeft(box.x);
34774             this.split.el.setTop(box.y-sh);
34775             this.split.el.setWidth(box.width);
34776         }
34777         if(this.collapsed){
34778             this.updateBody(box.width, null);
34779         }
34780         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34781     }
34782 });
34783
34784 Roo.EastLayoutRegion = function(mgr, config){
34785     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34786     if(this.split){
34787         this.split.placement = Roo.SplitBar.RIGHT;
34788         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34789         this.split.el.addClass("x-layout-split-h");
34790     }
34791     var size = config.initialSize || config.width;
34792     if(typeof size != "undefined"){
34793         this.el.setWidth(size);
34794     }
34795 };
34796 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34797     orientation: Roo.SplitBar.HORIZONTAL,
34798     getBox : function(){
34799         if(this.collapsed){
34800             return this.collapsedEl.getBox();
34801         }
34802         var box = this.el.getBox();
34803         if(this.split){
34804             var sw = this.split.el.getWidth();
34805             box.width += sw;
34806             box.x -= sw;
34807         }
34808         return box;
34809     },
34810
34811     updateBox : function(box){
34812         if(this.split && !this.collapsed){
34813             var sw = this.split.el.getWidth();
34814             box.width -= sw;
34815             this.split.el.setLeft(box.x);
34816             this.split.el.setTop(box.y);
34817             this.split.el.setHeight(box.height);
34818             box.x += sw;
34819         }
34820         if(this.collapsed){
34821             this.updateBody(null, box.height);
34822         }
34823         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34824     }
34825 });
34826
34827 Roo.WestLayoutRegion = function(mgr, config){
34828     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34829     if(this.split){
34830         this.split.placement = Roo.SplitBar.LEFT;
34831         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34832         this.split.el.addClass("x-layout-split-h");
34833     }
34834     var size = config.initialSize || config.width;
34835     if(typeof size != "undefined"){
34836         this.el.setWidth(size);
34837     }
34838 };
34839 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34840     orientation: Roo.SplitBar.HORIZONTAL,
34841     getBox : function(){
34842         if(this.collapsed){
34843             return this.collapsedEl.getBox();
34844         }
34845         var box = this.el.getBox();
34846         if(this.split){
34847             box.width += this.split.el.getWidth();
34848         }
34849         return box;
34850     },
34851     
34852     updateBox : function(box){
34853         if(this.split && !this.collapsed){
34854             var sw = this.split.el.getWidth();
34855             box.width -= sw;
34856             this.split.el.setLeft(box.x+box.width);
34857             this.split.el.setTop(box.y);
34858             this.split.el.setHeight(box.height);
34859         }
34860         if(this.collapsed){
34861             this.updateBody(null, box.height);
34862         }
34863         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34864     }
34865 });
34866 /*
34867  * Based on:
34868  * Ext JS Library 1.1.1
34869  * Copyright(c) 2006-2007, Ext JS, LLC.
34870  *
34871  * Originally Released Under LGPL - original licence link has changed is not relivant.
34872  *
34873  * Fork - LGPL
34874  * <script type="text/javascript">
34875  */
34876  
34877  
34878 /*
34879  * Private internal class for reading and applying state
34880  */
34881 Roo.LayoutStateManager = function(layout){
34882      // default empty state
34883      this.state = {
34884         north: {},
34885         south: {},
34886         east: {},
34887         west: {}       
34888     };
34889 };
34890
34891 Roo.LayoutStateManager.prototype = {
34892     init : function(layout, provider){
34893         this.provider = provider;
34894         var state = provider.get(layout.id+"-layout-state");
34895         if(state){
34896             var wasUpdating = layout.isUpdating();
34897             if(!wasUpdating){
34898                 layout.beginUpdate();
34899             }
34900             for(var key in state){
34901                 if(typeof state[key] != "function"){
34902                     var rstate = state[key];
34903                     var r = layout.getRegion(key);
34904                     if(r && rstate){
34905                         if(rstate.size){
34906                             r.resizeTo(rstate.size);
34907                         }
34908                         if(rstate.collapsed == true){
34909                             r.collapse(true);
34910                         }else{
34911                             r.expand(null, true);
34912                         }
34913                     }
34914                 }
34915             }
34916             if(!wasUpdating){
34917                 layout.endUpdate();
34918             }
34919             this.state = state; 
34920         }
34921         this.layout = layout;
34922         layout.on("regionresized", this.onRegionResized, this);
34923         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34924         layout.on("regionexpanded", this.onRegionExpanded, this);
34925     },
34926     
34927     storeState : function(){
34928         this.provider.set(this.layout.id+"-layout-state", this.state);
34929     },
34930     
34931     onRegionResized : function(region, newSize){
34932         this.state[region.getPosition()].size = newSize;
34933         this.storeState();
34934     },
34935     
34936     onRegionCollapsed : function(region){
34937         this.state[region.getPosition()].collapsed = true;
34938         this.storeState();
34939     },
34940     
34941     onRegionExpanded : function(region){
34942         this.state[region.getPosition()].collapsed = false;
34943         this.storeState();
34944     }
34945 };/*
34946  * Based on:
34947  * Ext JS Library 1.1.1
34948  * Copyright(c) 2006-2007, Ext JS, LLC.
34949  *
34950  * Originally Released Under LGPL - original licence link has changed is not relivant.
34951  *
34952  * Fork - LGPL
34953  * <script type="text/javascript">
34954  */
34955 /**
34956  * @class Roo.ContentPanel
34957  * @extends Roo.util.Observable
34958  * A basic ContentPanel element.
34959  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34960  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34961  * @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
34962  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34963  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34964  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34965  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34966  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34967  * @cfg {String} title          The title for this panel
34968  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34969  * @cfg {String} url            Calls {@link #setUrl} with this value
34970  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34971  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34972  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34973  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34974
34975  * @constructor
34976  * Create a new ContentPanel.
34977  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34978  * @param {String/Object} config A string to set only the title or a config object
34979  * @param {String} content (optional) Set the HTML content for this panel
34980  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34981  */
34982 Roo.ContentPanel = function(el, config, content){
34983     
34984      
34985     /*
34986     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34987         config = el;
34988         el = Roo.id();
34989     }
34990     if (config && config.parentLayout) { 
34991         el = config.parentLayout.el.createChild(); 
34992     }
34993     */
34994     if(el.autoCreate){ // xtype is available if this is called from factory
34995         config = el;
34996         el = Roo.id();
34997     }
34998     this.el = Roo.get(el);
34999     if(!this.el && config && config.autoCreate){
35000         if(typeof config.autoCreate == "object"){
35001             if(!config.autoCreate.id){
35002                 config.autoCreate.id = config.id||el;
35003             }
35004             this.el = Roo.DomHelper.append(document.body,
35005                         config.autoCreate, true);
35006         }else{
35007             this.el = Roo.DomHelper.append(document.body,
35008                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35009         }
35010     }
35011     this.closable = false;
35012     this.loaded = false;
35013     this.active = false;
35014     if(typeof config == "string"){
35015         this.title = config;
35016     }else{
35017         Roo.apply(this, config);
35018     }
35019     
35020     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35021         this.wrapEl = this.el.wrap();
35022         this.toolbar.container = this.el.insertSibling(false, 'before');
35023         this.toolbar = new Roo.Toolbar(this.toolbar);
35024     }
35025     
35026     // xtype created footer. - not sure if will work as we normally have to render first..
35027     if (this.footer && !this.footer.el && this.footer.xtype) {
35028         if (!this.wrapEl) {
35029             this.wrapEl = this.el.wrap();
35030         }
35031     
35032         this.footer.container = this.wrapEl.createChild();
35033          
35034         this.footer = Roo.factory(this.footer, Roo);
35035         
35036     }
35037     
35038     if(this.resizeEl){
35039         this.resizeEl = Roo.get(this.resizeEl, true);
35040     }else{
35041         this.resizeEl = this.el;
35042     }
35043     // handle view.xtype
35044     
35045  
35046     
35047     
35048     this.addEvents({
35049         /**
35050          * @event activate
35051          * Fires when this panel is activated. 
35052          * @param {Roo.ContentPanel} this
35053          */
35054         "activate" : true,
35055         /**
35056          * @event deactivate
35057          * Fires when this panel is activated. 
35058          * @param {Roo.ContentPanel} this
35059          */
35060         "deactivate" : true,
35061
35062         /**
35063          * @event resize
35064          * Fires when this panel is resized if fitToFrame is true.
35065          * @param {Roo.ContentPanel} this
35066          * @param {Number} width The width after any component adjustments
35067          * @param {Number} height The height after any component adjustments
35068          */
35069         "resize" : true,
35070         
35071          /**
35072          * @event render
35073          * Fires when this tab is created
35074          * @param {Roo.ContentPanel} this
35075          */
35076         "render" : true
35077         
35078         
35079         
35080     });
35081     
35082
35083     
35084     
35085     if(this.autoScroll){
35086         this.resizeEl.setStyle("overflow", "auto");
35087     } else {
35088         // fix randome scrolling
35089         this.el.on('scroll', function() {
35090             Roo.log('fix random scolling');
35091             this.scrollTo('top',0); 
35092         });
35093     }
35094     content = content || this.content;
35095     if(content){
35096         this.setContent(content);
35097     }
35098     if(config && config.url){
35099         this.setUrl(this.url, this.params, this.loadOnce);
35100     }
35101     
35102     
35103     
35104     Roo.ContentPanel.superclass.constructor.call(this);
35105     
35106     if (this.view && typeof(this.view.xtype) != 'undefined') {
35107         this.view.el = this.el.appendChild(document.createElement("div"));
35108         this.view = Roo.factory(this.view); 
35109         this.view.render  &&  this.view.render(false, '');  
35110     }
35111     
35112     
35113     this.fireEvent('render', this);
35114 };
35115
35116 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35117     tabTip:'',
35118     setRegion : function(region){
35119         this.region = region;
35120         if(region){
35121            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35122         }else{
35123            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35124         } 
35125     },
35126     
35127     /**
35128      * Returns the toolbar for this Panel if one was configured. 
35129      * @return {Roo.Toolbar} 
35130      */
35131     getToolbar : function(){
35132         return this.toolbar;
35133     },
35134     
35135     setActiveState : function(active){
35136         this.active = active;
35137         if(!active){
35138             this.fireEvent("deactivate", this);
35139         }else{
35140             this.fireEvent("activate", this);
35141         }
35142     },
35143     /**
35144      * Updates this panel's element
35145      * @param {String} content The new content
35146      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35147     */
35148     setContent : function(content, loadScripts){
35149         this.el.update(content, loadScripts);
35150     },
35151
35152     ignoreResize : function(w, h){
35153         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35154             return true;
35155         }else{
35156             this.lastSize = {width: w, height: h};
35157             return false;
35158         }
35159     },
35160     /**
35161      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35162      * @return {Roo.UpdateManager} The UpdateManager
35163      */
35164     getUpdateManager : function(){
35165         return this.el.getUpdateManager();
35166     },
35167      /**
35168      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35169      * @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:
35170 <pre><code>
35171 panel.load({
35172     url: "your-url.php",
35173     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35174     callback: yourFunction,
35175     scope: yourObject, //(optional scope)
35176     discardUrl: false,
35177     nocache: false,
35178     text: "Loading...",
35179     timeout: 30,
35180     scripts: false
35181 });
35182 </code></pre>
35183      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35184      * 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.
35185      * @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}
35186      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35187      * @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.
35188      * @return {Roo.ContentPanel} this
35189      */
35190     load : function(){
35191         var um = this.el.getUpdateManager();
35192         um.update.apply(um, arguments);
35193         return this;
35194     },
35195
35196
35197     /**
35198      * 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.
35199      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35200      * @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)
35201      * @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)
35202      * @return {Roo.UpdateManager} The UpdateManager
35203      */
35204     setUrl : function(url, params, loadOnce){
35205         if(this.refreshDelegate){
35206             this.removeListener("activate", this.refreshDelegate);
35207         }
35208         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35209         this.on("activate", this.refreshDelegate);
35210         return this.el.getUpdateManager();
35211     },
35212     
35213     _handleRefresh : function(url, params, loadOnce){
35214         if(!loadOnce || !this.loaded){
35215             var updater = this.el.getUpdateManager();
35216             updater.update(url, params, this._setLoaded.createDelegate(this));
35217         }
35218     },
35219     
35220     _setLoaded : function(){
35221         this.loaded = true;
35222     }, 
35223     
35224     /**
35225      * Returns this panel's id
35226      * @return {String} 
35227      */
35228     getId : function(){
35229         return this.el.id;
35230     },
35231     
35232     /** 
35233      * Returns this panel's element - used by regiosn to add.
35234      * @return {Roo.Element} 
35235      */
35236     getEl : function(){
35237         return this.wrapEl || this.el;
35238     },
35239     
35240     adjustForComponents : function(width, height)
35241     {
35242         //Roo.log('adjustForComponents ');
35243         if(this.resizeEl != this.el){
35244             width -= this.el.getFrameWidth('lr');
35245             height -= this.el.getFrameWidth('tb');
35246         }
35247         if(this.toolbar){
35248             var te = this.toolbar.getEl();
35249             height -= te.getHeight();
35250             te.setWidth(width);
35251         }
35252         if(this.footer){
35253             var te = this.footer.getEl();
35254             Roo.log("footer:" + te.getHeight());
35255             
35256             height -= te.getHeight();
35257             te.setWidth(width);
35258         }
35259         
35260         
35261         if(this.adjustments){
35262             width += this.adjustments[0];
35263             height += this.adjustments[1];
35264         }
35265         return {"width": width, "height": height};
35266     },
35267     
35268     setSize : function(width, height){
35269         if(this.fitToFrame && !this.ignoreResize(width, height)){
35270             if(this.fitContainer && this.resizeEl != this.el){
35271                 this.el.setSize(width, height);
35272             }
35273             var size = this.adjustForComponents(width, height);
35274             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35275             this.fireEvent('resize', this, size.width, size.height);
35276         }
35277     },
35278     
35279     /**
35280      * Returns this panel's title
35281      * @return {String} 
35282      */
35283     getTitle : function(){
35284         return this.title;
35285     },
35286     
35287     /**
35288      * Set this panel's title
35289      * @param {String} title
35290      */
35291     setTitle : function(title){
35292         this.title = title;
35293         if(this.region){
35294             this.region.updatePanelTitle(this, title);
35295         }
35296     },
35297     
35298     /**
35299      * Returns true is this panel was configured to be closable
35300      * @return {Boolean} 
35301      */
35302     isClosable : function(){
35303         return this.closable;
35304     },
35305     
35306     beforeSlide : function(){
35307         this.el.clip();
35308         this.resizeEl.clip();
35309     },
35310     
35311     afterSlide : function(){
35312         this.el.unclip();
35313         this.resizeEl.unclip();
35314     },
35315     
35316     /**
35317      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35318      *   Will fail silently if the {@link #setUrl} method has not been called.
35319      *   This does not activate the panel, just updates its content.
35320      */
35321     refresh : function(){
35322         if(this.refreshDelegate){
35323            this.loaded = false;
35324            this.refreshDelegate();
35325         }
35326     },
35327     
35328     /**
35329      * Destroys this panel
35330      */
35331     destroy : function(){
35332         this.el.removeAllListeners();
35333         var tempEl = document.createElement("span");
35334         tempEl.appendChild(this.el.dom);
35335         tempEl.innerHTML = "";
35336         this.el.remove();
35337         this.el = null;
35338     },
35339     
35340     /**
35341      * form - if the content panel contains a form - this is a reference to it.
35342      * @type {Roo.form.Form}
35343      */
35344     form : false,
35345     /**
35346      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35347      *    This contains a reference to it.
35348      * @type {Roo.View}
35349      */
35350     view : false,
35351     
35352       /**
35353      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35354      * <pre><code>
35355
35356 layout.addxtype({
35357        xtype : 'Form',
35358        items: [ .... ]
35359    }
35360 );
35361
35362 </code></pre>
35363      * @param {Object} cfg Xtype definition of item to add.
35364      */
35365     
35366     addxtype : function(cfg) {
35367         // add form..
35368         if (cfg.xtype.match(/^Form$/)) {
35369             
35370             var el;
35371             //if (this.footer) {
35372             //    el = this.footer.container.insertSibling(false, 'before');
35373             //} else {
35374                 el = this.el.createChild();
35375             //}
35376
35377             this.form = new  Roo.form.Form(cfg);
35378             
35379             
35380             if ( this.form.allItems.length) this.form.render(el.dom);
35381             return this.form;
35382         }
35383         // should only have one of theses..
35384         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35385             // views.. should not be just added - used named prop 'view''
35386             
35387             cfg.el = this.el.appendChild(document.createElement("div"));
35388             // factory?
35389             
35390             var ret = new Roo.factory(cfg);
35391              
35392              ret.render && ret.render(false, ''); // render blank..
35393             this.view = ret;
35394             return ret;
35395         }
35396         return false;
35397     }
35398 });
35399
35400 /**
35401  * @class Roo.GridPanel
35402  * @extends Roo.ContentPanel
35403  * @constructor
35404  * Create a new GridPanel.
35405  * @param {Roo.grid.Grid} grid The grid for this panel
35406  * @param {String/Object} config A string to set only the panel's title, or a config object
35407  */
35408 Roo.GridPanel = function(grid, config){
35409     
35410   
35411     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35412         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35413         
35414     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35415     
35416     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35417     
35418     if(this.toolbar){
35419         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35420     }
35421     // xtype created footer. - not sure if will work as we normally have to render first..
35422     if (this.footer && !this.footer.el && this.footer.xtype) {
35423         
35424         this.footer.container = this.grid.getView().getFooterPanel(true);
35425         this.footer.dataSource = this.grid.dataSource;
35426         this.footer = Roo.factory(this.footer, Roo);
35427         
35428     }
35429     
35430     grid.monitorWindowResize = false; // turn off autosizing
35431     grid.autoHeight = false;
35432     grid.autoWidth = false;
35433     this.grid = grid;
35434     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35435 };
35436
35437 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35438     getId : function(){
35439         return this.grid.id;
35440     },
35441     
35442     /**
35443      * Returns the grid for this panel
35444      * @return {Roo.grid.Grid} 
35445      */
35446     getGrid : function(){
35447         return this.grid;    
35448     },
35449     
35450     setSize : function(width, height){
35451         if(!this.ignoreResize(width, height)){
35452             var grid = this.grid;
35453             var size = this.adjustForComponents(width, height);
35454             grid.getGridEl().setSize(size.width, size.height);
35455             grid.autoSize();
35456         }
35457     },
35458     
35459     beforeSlide : function(){
35460         this.grid.getView().scroller.clip();
35461     },
35462     
35463     afterSlide : function(){
35464         this.grid.getView().scroller.unclip();
35465     },
35466     
35467     destroy : function(){
35468         this.grid.destroy();
35469         delete this.grid;
35470         Roo.GridPanel.superclass.destroy.call(this); 
35471     }
35472 });
35473
35474
35475 /**
35476  * @class Roo.NestedLayoutPanel
35477  * @extends Roo.ContentPanel
35478  * @constructor
35479  * Create a new NestedLayoutPanel.
35480  * 
35481  * 
35482  * @param {Roo.BorderLayout} layout The layout for this panel
35483  * @param {String/Object} config A string to set only the title or a config object
35484  */
35485 Roo.NestedLayoutPanel = function(layout, config)
35486 {
35487     // construct with only one argument..
35488     /* FIXME - implement nicer consturctors
35489     if (layout.layout) {
35490         config = layout;
35491         layout = config.layout;
35492         delete config.layout;
35493     }
35494     if (layout.xtype && !layout.getEl) {
35495         // then layout needs constructing..
35496         layout = Roo.factory(layout, Roo);
35497     }
35498     */
35499     
35500     
35501     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35502     
35503     layout.monitorWindowResize = false; // turn off autosizing
35504     this.layout = layout;
35505     this.layout.getEl().addClass("x-layout-nested-layout");
35506     
35507     
35508     
35509     
35510 };
35511
35512 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35513
35514     setSize : function(width, height){
35515         if(!this.ignoreResize(width, height)){
35516             var size = this.adjustForComponents(width, height);
35517             var el = this.layout.getEl();
35518             el.setSize(size.width, size.height);
35519             var touch = el.dom.offsetWidth;
35520             this.layout.layout();
35521             // ie requires a double layout on the first pass
35522             if(Roo.isIE && !this.initialized){
35523                 this.initialized = true;
35524                 this.layout.layout();
35525             }
35526         }
35527     },
35528     
35529     // activate all subpanels if not currently active..
35530     
35531     setActiveState : function(active){
35532         this.active = active;
35533         if(!active){
35534             this.fireEvent("deactivate", this);
35535             return;
35536         }
35537         
35538         this.fireEvent("activate", this);
35539         // not sure if this should happen before or after..
35540         if (!this.layout) {
35541             return; // should not happen..
35542         }
35543         var reg = false;
35544         for (var r in this.layout.regions) {
35545             reg = this.layout.getRegion(r);
35546             if (reg.getActivePanel()) {
35547                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35548                 reg.setActivePanel(reg.getActivePanel());
35549                 continue;
35550             }
35551             if (!reg.panels.length) {
35552                 continue;
35553             }
35554             reg.showPanel(reg.getPanel(0));
35555         }
35556         
35557         
35558         
35559         
35560     },
35561     
35562     /**
35563      * Returns the nested BorderLayout for this panel
35564      * @return {Roo.BorderLayout} 
35565      */
35566     getLayout : function(){
35567         return this.layout;
35568     },
35569     
35570      /**
35571      * Adds a xtype elements to the layout of the nested panel
35572      * <pre><code>
35573
35574 panel.addxtype({
35575        xtype : 'ContentPanel',
35576        region: 'west',
35577        items: [ .... ]
35578    }
35579 );
35580
35581 panel.addxtype({
35582         xtype : 'NestedLayoutPanel',
35583         region: 'west',
35584         layout: {
35585            center: { },
35586            west: { }   
35587         },
35588         items : [ ... list of content panels or nested layout panels.. ]
35589    }
35590 );
35591 </code></pre>
35592      * @param {Object} cfg Xtype definition of item to add.
35593      */
35594     addxtype : function(cfg) {
35595         return this.layout.addxtype(cfg);
35596     
35597     }
35598 });
35599
35600 Roo.ScrollPanel = function(el, config, content){
35601     config = config || {};
35602     config.fitToFrame = true;
35603     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35604     
35605     this.el.dom.style.overflow = "hidden";
35606     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35607     this.el.removeClass("x-layout-inactive-content");
35608     this.el.on("mousewheel", this.onWheel, this);
35609
35610     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35611     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35612     up.unselectable(); down.unselectable();
35613     up.on("click", this.scrollUp, this);
35614     down.on("click", this.scrollDown, this);
35615     up.addClassOnOver("x-scroller-btn-over");
35616     down.addClassOnOver("x-scroller-btn-over");
35617     up.addClassOnClick("x-scroller-btn-click");
35618     down.addClassOnClick("x-scroller-btn-click");
35619     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35620
35621     this.resizeEl = this.el;
35622     this.el = wrap; this.up = up; this.down = down;
35623 };
35624
35625 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35626     increment : 100,
35627     wheelIncrement : 5,
35628     scrollUp : function(){
35629         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35630     },
35631
35632     scrollDown : function(){
35633         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35634     },
35635
35636     afterScroll : function(){
35637         var el = this.resizeEl;
35638         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35639         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35640         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35641     },
35642
35643     setSize : function(){
35644         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35645         this.afterScroll();
35646     },
35647
35648     onWheel : function(e){
35649         var d = e.getWheelDelta();
35650         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35651         this.afterScroll();
35652         e.stopEvent();
35653     },
35654
35655     setContent : function(content, loadScripts){
35656         this.resizeEl.update(content, loadScripts);
35657     }
35658
35659 });
35660
35661
35662
35663
35664
35665
35666
35667
35668
35669 /**
35670  * @class Roo.TreePanel
35671  * @extends Roo.ContentPanel
35672  * @constructor
35673  * Create a new TreePanel. - defaults to fit/scoll contents.
35674  * @param {String/Object} config A string to set only the panel's title, or a config object
35675  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35676  */
35677 Roo.TreePanel = function(config){
35678     var el = config.el;
35679     var tree = config.tree;
35680     delete config.tree; 
35681     delete config.el; // hopefull!
35682     
35683     // wrapper for IE7 strict & safari scroll issue
35684     
35685     var treeEl = el.createChild();
35686     config.resizeEl = treeEl;
35687     
35688     
35689     
35690     Roo.TreePanel.superclass.constructor.call(this, el, config);
35691  
35692  
35693     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35694     //console.log(tree);
35695     this.on('activate', function()
35696     {
35697         if (this.tree.rendered) {
35698             return;
35699         }
35700         //console.log('render tree');
35701         this.tree.render();
35702     });
35703     // this should not be needed.. - it's actually the 'el' that resizes?
35704     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35705     
35706     //this.on('resize',  function (cp, w, h) {
35707     //        this.tree.innerCt.setWidth(w);
35708     //        this.tree.innerCt.setHeight(h);
35709     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35710     //});
35711
35712         
35713     
35714 };
35715
35716 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35717     fitToFrame : true,
35718     autoScroll : true
35719 });
35720
35721
35722
35723
35724
35725
35726
35727
35728
35729
35730
35731 /*
35732  * Based on:
35733  * Ext JS Library 1.1.1
35734  * Copyright(c) 2006-2007, Ext JS, LLC.
35735  *
35736  * Originally Released Under LGPL - original licence link has changed is not relivant.
35737  *
35738  * Fork - LGPL
35739  * <script type="text/javascript">
35740  */
35741  
35742
35743 /**
35744  * @class Roo.ReaderLayout
35745  * @extends Roo.BorderLayout
35746  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35747  * center region containing two nested regions (a top one for a list view and one for item preview below),
35748  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35749  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35750  * expedites the setup of the overall layout and regions for this common application style.
35751  * Example:
35752  <pre><code>
35753 var reader = new Roo.ReaderLayout();
35754 var CP = Roo.ContentPanel;  // shortcut for adding
35755
35756 reader.beginUpdate();
35757 reader.add("north", new CP("north", "North"));
35758 reader.add("west", new CP("west", {title: "West"}));
35759 reader.add("east", new CP("east", {title: "East"}));
35760
35761 reader.regions.listView.add(new CP("listView", "List"));
35762 reader.regions.preview.add(new CP("preview", "Preview"));
35763 reader.endUpdate();
35764 </code></pre>
35765 * @constructor
35766 * Create a new ReaderLayout
35767 * @param {Object} config Configuration options
35768 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35769 * document.body if omitted)
35770 */
35771 Roo.ReaderLayout = function(config, renderTo){
35772     var c = config || {size:{}};
35773     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35774         north: c.north !== false ? Roo.apply({
35775             split:false,
35776             initialSize: 32,
35777             titlebar: false
35778         }, c.north) : false,
35779         west: c.west !== false ? Roo.apply({
35780             split:true,
35781             initialSize: 200,
35782             minSize: 175,
35783             maxSize: 400,
35784             titlebar: true,
35785             collapsible: true,
35786             animate: true,
35787             margins:{left:5,right:0,bottom:5,top:5},
35788             cmargins:{left:5,right:5,bottom:5,top:5}
35789         }, c.west) : false,
35790         east: c.east !== false ? Roo.apply({
35791             split:true,
35792             initialSize: 200,
35793             minSize: 175,
35794             maxSize: 400,
35795             titlebar: true,
35796             collapsible: true,
35797             animate: true,
35798             margins:{left:0,right:5,bottom:5,top:5},
35799             cmargins:{left:5,right:5,bottom:5,top:5}
35800         }, c.east) : false,
35801         center: Roo.apply({
35802             tabPosition: 'top',
35803             autoScroll:false,
35804             closeOnTab: true,
35805             titlebar:false,
35806             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35807         }, c.center)
35808     });
35809
35810     this.el.addClass('x-reader');
35811
35812     this.beginUpdate();
35813
35814     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35815         south: c.preview !== false ? Roo.apply({
35816             split:true,
35817             initialSize: 200,
35818             minSize: 100,
35819             autoScroll:true,
35820             collapsible:true,
35821             titlebar: true,
35822             cmargins:{top:5,left:0, right:0, bottom:0}
35823         }, c.preview) : false,
35824         center: Roo.apply({
35825             autoScroll:false,
35826             titlebar:false,
35827             minHeight:200
35828         }, c.listView)
35829     });
35830     this.add('center', new Roo.NestedLayoutPanel(inner,
35831             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35832
35833     this.endUpdate();
35834
35835     this.regions.preview = inner.getRegion('south');
35836     this.regions.listView = inner.getRegion('center');
35837 };
35838
35839 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35840  * Based on:
35841  * Ext JS Library 1.1.1
35842  * Copyright(c) 2006-2007, Ext JS, LLC.
35843  *
35844  * Originally Released Under LGPL - original licence link has changed is not relivant.
35845  *
35846  * Fork - LGPL
35847  * <script type="text/javascript">
35848  */
35849  
35850 /**
35851  * @class Roo.grid.Grid
35852  * @extends Roo.util.Observable
35853  * This class represents the primary interface of a component based grid control.
35854  * <br><br>Usage:<pre><code>
35855  var grid = new Roo.grid.Grid("my-container-id", {
35856      ds: myDataStore,
35857      cm: myColModel,
35858      selModel: mySelectionModel,
35859      autoSizeColumns: true,
35860      monitorWindowResize: false,
35861      trackMouseOver: true
35862  });
35863  // set any options
35864  grid.render();
35865  * </code></pre>
35866  * <b>Common Problems:</b><br/>
35867  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35868  * element will correct this<br/>
35869  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35870  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35871  * are unpredictable.<br/>
35872  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35873  * grid to calculate dimensions/offsets.<br/>
35874   * @constructor
35875  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35876  * The container MUST have some type of size defined for the grid to fill. The container will be
35877  * automatically set to position relative if it isn't already.
35878  * @param {Object} config A config object that sets properties on this grid.
35879  */
35880 Roo.grid.Grid = function(container, config){
35881         // initialize the container
35882         this.container = Roo.get(container);
35883         this.container.update("");
35884         this.container.setStyle("overflow", "hidden");
35885     this.container.addClass('x-grid-container');
35886
35887     this.id = this.container.id;
35888
35889     Roo.apply(this, config);
35890     // check and correct shorthanded configs
35891     if(this.ds){
35892         this.dataSource = this.ds;
35893         delete this.ds;
35894     }
35895     if(this.cm){
35896         this.colModel = this.cm;
35897         delete this.cm;
35898     }
35899     if(this.sm){
35900         this.selModel = this.sm;
35901         delete this.sm;
35902     }
35903
35904     if (this.selModel) {
35905         this.selModel = Roo.factory(this.selModel, Roo.grid);
35906         this.sm = this.selModel;
35907         this.sm.xmodule = this.xmodule || false;
35908     }
35909     if (typeof(this.colModel.config) == 'undefined') {
35910         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35911         this.cm = this.colModel;
35912         this.cm.xmodule = this.xmodule || false;
35913     }
35914     if (this.dataSource) {
35915         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35916         this.ds = this.dataSource;
35917         this.ds.xmodule = this.xmodule || false;
35918          
35919     }
35920     
35921     
35922     
35923     if(this.width){
35924         this.container.setWidth(this.width);
35925     }
35926
35927     if(this.height){
35928         this.container.setHeight(this.height);
35929     }
35930     /** @private */
35931         this.addEvents({
35932         // raw events
35933         /**
35934          * @event click
35935          * The raw click event for the entire grid.
35936          * @param {Roo.EventObject} e
35937          */
35938         "click" : true,
35939         /**
35940          * @event dblclick
35941          * The raw dblclick event for the entire grid.
35942          * @param {Roo.EventObject} e
35943          */
35944         "dblclick" : true,
35945         /**
35946          * @event contextmenu
35947          * The raw contextmenu event for the entire grid.
35948          * @param {Roo.EventObject} e
35949          */
35950         "contextmenu" : true,
35951         /**
35952          * @event mousedown
35953          * The raw mousedown event for the entire grid.
35954          * @param {Roo.EventObject} e
35955          */
35956         "mousedown" : true,
35957         /**
35958          * @event mouseup
35959          * The raw mouseup event for the entire grid.
35960          * @param {Roo.EventObject} e
35961          */
35962         "mouseup" : true,
35963         /**
35964          * @event mouseover
35965          * The raw mouseover event for the entire grid.
35966          * @param {Roo.EventObject} e
35967          */
35968         "mouseover" : true,
35969         /**
35970          * @event mouseout
35971          * The raw mouseout event for the entire grid.
35972          * @param {Roo.EventObject} e
35973          */
35974         "mouseout" : true,
35975         /**
35976          * @event keypress
35977          * The raw keypress event for the entire grid.
35978          * @param {Roo.EventObject} e
35979          */
35980         "keypress" : true,
35981         /**
35982          * @event keydown
35983          * The raw keydown event for the entire grid.
35984          * @param {Roo.EventObject} e
35985          */
35986         "keydown" : true,
35987
35988         // custom events
35989
35990         /**
35991          * @event cellclick
35992          * Fires when a cell is clicked
35993          * @param {Grid} this
35994          * @param {Number} rowIndex
35995          * @param {Number} columnIndex
35996          * @param {Roo.EventObject} e
35997          */
35998         "cellclick" : true,
35999         /**
36000          * @event celldblclick
36001          * Fires when a cell is double clicked
36002          * @param {Grid} this
36003          * @param {Number} rowIndex
36004          * @param {Number} columnIndex
36005          * @param {Roo.EventObject} e
36006          */
36007         "celldblclick" : true,
36008         /**
36009          * @event rowclick
36010          * Fires when a row is clicked
36011          * @param {Grid} this
36012          * @param {Number} rowIndex
36013          * @param {Roo.EventObject} e
36014          */
36015         "rowclick" : true,
36016         /**
36017          * @event rowdblclick
36018          * Fires when a row is double clicked
36019          * @param {Grid} this
36020          * @param {Number} rowIndex
36021          * @param {Roo.EventObject} e
36022          */
36023         "rowdblclick" : true,
36024         /**
36025          * @event headerclick
36026          * Fires when a header is clicked
36027          * @param {Grid} this
36028          * @param {Number} columnIndex
36029          * @param {Roo.EventObject} e
36030          */
36031         "headerclick" : true,
36032         /**
36033          * @event headerdblclick
36034          * Fires when a header cell is double clicked
36035          * @param {Grid} this
36036          * @param {Number} columnIndex
36037          * @param {Roo.EventObject} e
36038          */
36039         "headerdblclick" : true,
36040         /**
36041          * @event rowcontextmenu
36042          * Fires when a row is right clicked
36043          * @param {Grid} this
36044          * @param {Number} rowIndex
36045          * @param {Roo.EventObject} e
36046          */
36047         "rowcontextmenu" : true,
36048         /**
36049          * @event cellcontextmenu
36050          * Fires when a cell is right clicked
36051          * @param {Grid} this
36052          * @param {Number} rowIndex
36053          * @param {Number} cellIndex
36054          * @param {Roo.EventObject} e
36055          */
36056          "cellcontextmenu" : true,
36057         /**
36058          * @event headercontextmenu
36059          * Fires when a header is right clicked
36060          * @param {Grid} this
36061          * @param {Number} columnIndex
36062          * @param {Roo.EventObject} e
36063          */
36064         "headercontextmenu" : true,
36065         /**
36066          * @event bodyscroll
36067          * Fires when the body element is scrolled
36068          * @param {Number} scrollLeft
36069          * @param {Number} scrollTop
36070          */
36071         "bodyscroll" : true,
36072         /**
36073          * @event columnresize
36074          * Fires when the user resizes a column
36075          * @param {Number} columnIndex
36076          * @param {Number} newSize
36077          */
36078         "columnresize" : true,
36079         /**
36080          * @event columnmove
36081          * Fires when the user moves a column
36082          * @param {Number} oldIndex
36083          * @param {Number} newIndex
36084          */
36085         "columnmove" : true,
36086         /**
36087          * @event startdrag
36088          * Fires when row(s) start being dragged
36089          * @param {Grid} this
36090          * @param {Roo.GridDD} dd The drag drop object
36091          * @param {event} e The raw browser event
36092          */
36093         "startdrag" : true,
36094         /**
36095          * @event enddrag
36096          * Fires when a drag operation is complete
36097          * @param {Grid} this
36098          * @param {Roo.GridDD} dd The drag drop object
36099          * @param {event} e The raw browser event
36100          */
36101         "enddrag" : true,
36102         /**
36103          * @event dragdrop
36104          * Fires when dragged row(s) are dropped on a valid DD target
36105          * @param {Grid} this
36106          * @param {Roo.GridDD} dd The drag drop object
36107          * @param {String} targetId The target drag drop object
36108          * @param {event} e The raw browser event
36109          */
36110         "dragdrop" : true,
36111         /**
36112          * @event dragover
36113          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36114          * @param {Grid} this
36115          * @param {Roo.GridDD} dd The drag drop object
36116          * @param {String} targetId The target drag drop object
36117          * @param {event} e The raw browser event
36118          */
36119         "dragover" : true,
36120         /**
36121          * @event dragenter
36122          *  Fires when the dragged row(s) first cross another DD target while being dragged
36123          * @param {Grid} this
36124          * @param {Roo.GridDD} dd The drag drop object
36125          * @param {String} targetId The target drag drop object
36126          * @param {event} e The raw browser event
36127          */
36128         "dragenter" : true,
36129         /**
36130          * @event dragout
36131          * Fires when the dragged row(s) leave another DD target while being dragged
36132          * @param {Grid} this
36133          * @param {Roo.GridDD} dd The drag drop object
36134          * @param {String} targetId The target drag drop object
36135          * @param {event} e The raw browser event
36136          */
36137         "dragout" : true,
36138         /**
36139          * @event rowclass
36140          * Fires when a row is rendered, so you can change add a style to it.
36141          * @param {GridView} gridview   The grid view
36142          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36143          */
36144         'rowclass' : true,
36145
36146         /**
36147          * @event render
36148          * Fires when the grid is rendered
36149          * @param {Grid} grid
36150          */
36151         'render' : true
36152     });
36153
36154     Roo.grid.Grid.superclass.constructor.call(this);
36155 };
36156 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36157     
36158     /**
36159      * @cfg {String} ddGroup - drag drop group.
36160      */
36161
36162     /**
36163      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36164      */
36165     minColumnWidth : 25,
36166
36167     /**
36168      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36169      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36170      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36171      */
36172     autoSizeColumns : false,
36173
36174     /**
36175      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36176      */
36177     autoSizeHeaders : true,
36178
36179     /**
36180      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36181      */
36182     monitorWindowResize : true,
36183
36184     /**
36185      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36186      * rows measured to get a columns size. Default is 0 (all rows).
36187      */
36188     maxRowsToMeasure : 0,
36189
36190     /**
36191      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36192      */
36193     trackMouseOver : true,
36194
36195     /**
36196     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36197     */
36198     
36199     /**
36200     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36201     */
36202     enableDragDrop : false,
36203     
36204     /**
36205     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36206     */
36207     enableColumnMove : true,
36208     
36209     /**
36210     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36211     */
36212     enableColumnHide : true,
36213     
36214     /**
36215     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36216     */
36217     enableRowHeightSync : false,
36218     
36219     /**
36220     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36221     */
36222     stripeRows : true,
36223     
36224     /**
36225     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36226     */
36227     autoHeight : false,
36228
36229     /**
36230      * @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.
36231      */
36232     autoExpandColumn : false,
36233
36234     /**
36235     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36236     * Default is 50.
36237     */
36238     autoExpandMin : 50,
36239
36240     /**
36241     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36242     */
36243     autoExpandMax : 1000,
36244
36245     /**
36246     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36247     */
36248     view : null,
36249
36250     /**
36251     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36252     */
36253     loadMask : false,
36254     /**
36255     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36256     */
36257     dropTarget: false,
36258     
36259    
36260     
36261     // private
36262     rendered : false,
36263
36264     /**
36265     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36266     * of a fixed width. Default is false.
36267     */
36268     /**
36269     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36270     */
36271     /**
36272      * Called once after all setup has been completed and the grid is ready to be rendered.
36273      * @return {Roo.grid.Grid} this
36274      */
36275     render : function()
36276     {
36277         var c = this.container;
36278         // try to detect autoHeight/width mode
36279         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36280             this.autoHeight = true;
36281         }
36282         var view = this.getView();
36283         view.init(this);
36284
36285         c.on("click", this.onClick, this);
36286         c.on("dblclick", this.onDblClick, this);
36287         c.on("contextmenu", this.onContextMenu, this);
36288         c.on("keydown", this.onKeyDown, this);
36289         if (Roo.isTouch) {
36290             c.on("touchstart", this.onTouchStart, this);
36291         }
36292
36293         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36294
36295         this.getSelectionModel().init(this);
36296
36297         view.render();
36298
36299         if(this.loadMask){
36300             this.loadMask = new Roo.LoadMask(this.container,
36301                     Roo.apply({store:this.dataSource}, this.loadMask));
36302         }
36303         
36304         
36305         if (this.toolbar && this.toolbar.xtype) {
36306             this.toolbar.container = this.getView().getHeaderPanel(true);
36307             this.toolbar = new Roo.Toolbar(this.toolbar);
36308         }
36309         if (this.footer && this.footer.xtype) {
36310             this.footer.dataSource = this.getDataSource();
36311             this.footer.container = this.getView().getFooterPanel(true);
36312             this.footer = Roo.factory(this.footer, Roo);
36313         }
36314         if (this.dropTarget && this.dropTarget.xtype) {
36315             delete this.dropTarget.xtype;
36316             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36317         }
36318         
36319         
36320         this.rendered = true;
36321         this.fireEvent('render', this);
36322         return this;
36323     },
36324
36325         /**
36326          * Reconfigures the grid to use a different Store and Column Model.
36327          * The View will be bound to the new objects and refreshed.
36328          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36329          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36330          */
36331     reconfigure : function(dataSource, colModel){
36332         if(this.loadMask){
36333             this.loadMask.destroy();
36334             this.loadMask = new Roo.LoadMask(this.container,
36335                     Roo.apply({store:dataSource}, this.loadMask));
36336         }
36337         this.view.bind(dataSource, colModel);
36338         this.dataSource = dataSource;
36339         this.colModel = colModel;
36340         this.view.refresh(true);
36341     },
36342
36343     // private
36344     onKeyDown : function(e){
36345         this.fireEvent("keydown", e);
36346     },
36347
36348     /**
36349      * Destroy this grid.
36350      * @param {Boolean} removeEl True to remove the element
36351      */
36352     destroy : function(removeEl, keepListeners){
36353         if(this.loadMask){
36354             this.loadMask.destroy();
36355         }
36356         var c = this.container;
36357         c.removeAllListeners();
36358         this.view.destroy();
36359         this.colModel.purgeListeners();
36360         if(!keepListeners){
36361             this.purgeListeners();
36362         }
36363         c.update("");
36364         if(removeEl === true){
36365             c.remove();
36366         }
36367     },
36368
36369     // private
36370     processEvent : function(name, e){
36371         // does this fire select???
36372         //Roo.log('grid:processEvent '  + name);
36373         
36374         if (name != 'touchstart' ) {
36375             this.fireEvent(name, e);    
36376         }
36377         
36378         var t = e.getTarget();
36379         var v = this.view;
36380         var header = v.findHeaderIndex(t);
36381         if(header !== false){
36382             var ename = name == 'touchstart' ? 'click' : name;
36383              
36384             this.fireEvent("header" + ename, this, header, e);
36385         }else{
36386             var row = v.findRowIndex(t);
36387             var cell = v.findCellIndex(t);
36388             if (name == 'touchstart') {
36389                 // first touch is always a click.
36390                 // hopefull this happens after selection is updated.?
36391                 name = false;
36392                 
36393                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36394                     var cs = this.selModel.getSelectedCell();
36395                     if (row == cs[0] && cell == cs[1]){
36396                         name = 'dblclick';
36397                     }
36398                 }
36399                 if (typeof(this.selModel.getSelections) != 'undefined') {
36400                     var cs = this.selModel.getSelections();
36401                     var ds = this.dataSource;
36402                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36403                         name = 'dblclick';
36404                     }
36405                 }
36406                 if (!name) {
36407                     return;
36408                 }
36409             }
36410             
36411             
36412             if(row !== false){
36413                 this.fireEvent("row" + name, this, row, e);
36414                 if(cell !== false){
36415                     this.fireEvent("cell" + name, this, row, cell, e);
36416                 }
36417             }
36418         }
36419     },
36420
36421     // private
36422     onClick : function(e){
36423         this.processEvent("click", e);
36424     },
36425    // private
36426     onTouchStart : function(e){
36427         this.processEvent("touchstart", e);
36428     },
36429
36430     // private
36431     onContextMenu : function(e, t){
36432         this.processEvent("contextmenu", e);
36433     },
36434
36435     // private
36436     onDblClick : function(e){
36437         this.processEvent("dblclick", e);
36438     },
36439
36440     // private
36441     walkCells : function(row, col, step, fn, scope){
36442         var cm = this.colModel, clen = cm.getColumnCount();
36443         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36444         if(step < 0){
36445             if(col < 0){
36446                 row--;
36447                 first = false;
36448             }
36449             while(row >= 0){
36450                 if(!first){
36451                     col = clen-1;
36452                 }
36453                 first = false;
36454                 while(col >= 0){
36455                     if(fn.call(scope || this, row, col, cm) === true){
36456                         return [row, col];
36457                     }
36458                     col--;
36459                 }
36460                 row--;
36461             }
36462         } else {
36463             if(col >= clen){
36464                 row++;
36465                 first = false;
36466             }
36467             while(row < rlen){
36468                 if(!first){
36469                     col = 0;
36470                 }
36471                 first = false;
36472                 while(col < clen){
36473                     if(fn.call(scope || this, row, col, cm) === true){
36474                         return [row, col];
36475                     }
36476                     col++;
36477                 }
36478                 row++;
36479             }
36480         }
36481         return null;
36482     },
36483
36484     // private
36485     getSelections : function(){
36486         return this.selModel.getSelections();
36487     },
36488
36489     /**
36490      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36491      * but if manual update is required this method will initiate it.
36492      */
36493     autoSize : function(){
36494         if(this.rendered){
36495             this.view.layout();
36496             if(this.view.adjustForScroll){
36497                 this.view.adjustForScroll();
36498             }
36499         }
36500     },
36501
36502     /**
36503      * Returns the grid's underlying element.
36504      * @return {Element} The element
36505      */
36506     getGridEl : function(){
36507         return this.container;
36508     },
36509
36510     // private for compatibility, overridden by editor grid
36511     stopEditing : function(){},
36512
36513     /**
36514      * Returns the grid's SelectionModel.
36515      * @return {SelectionModel}
36516      */
36517     getSelectionModel : function(){
36518         if(!this.selModel){
36519             this.selModel = new Roo.grid.RowSelectionModel();
36520         }
36521         return this.selModel;
36522     },
36523
36524     /**
36525      * Returns the grid's DataSource.
36526      * @return {DataSource}
36527      */
36528     getDataSource : function(){
36529         return this.dataSource;
36530     },
36531
36532     /**
36533      * Returns the grid's ColumnModel.
36534      * @return {ColumnModel}
36535      */
36536     getColumnModel : function(){
36537         return this.colModel;
36538     },
36539
36540     /**
36541      * Returns the grid's GridView object.
36542      * @return {GridView}
36543      */
36544     getView : function(){
36545         if(!this.view){
36546             this.view = new Roo.grid.GridView(this.viewConfig);
36547         }
36548         return this.view;
36549     },
36550     /**
36551      * Called to get grid's drag proxy text, by default returns this.ddText.
36552      * @return {String}
36553      */
36554     getDragDropText : function(){
36555         var count = this.selModel.getCount();
36556         return String.format(this.ddText, count, count == 1 ? '' : 's');
36557     }
36558 });
36559 /**
36560  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36561  * %0 is replaced with the number of selected rows.
36562  * @type String
36563  */
36564 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36565  * Based on:
36566  * Ext JS Library 1.1.1
36567  * Copyright(c) 2006-2007, Ext JS, LLC.
36568  *
36569  * Originally Released Under LGPL - original licence link has changed is not relivant.
36570  *
36571  * Fork - LGPL
36572  * <script type="text/javascript">
36573  */
36574  
36575 Roo.grid.AbstractGridView = function(){
36576         this.grid = null;
36577         
36578         this.events = {
36579             "beforerowremoved" : true,
36580             "beforerowsinserted" : true,
36581             "beforerefresh" : true,
36582             "rowremoved" : true,
36583             "rowsinserted" : true,
36584             "rowupdated" : true,
36585             "refresh" : true
36586         };
36587     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36588 };
36589
36590 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36591     rowClass : "x-grid-row",
36592     cellClass : "x-grid-cell",
36593     tdClass : "x-grid-td",
36594     hdClass : "x-grid-hd",
36595     splitClass : "x-grid-hd-split",
36596     
36597     init: function(grid){
36598         this.grid = grid;
36599                 var cid = this.grid.getGridEl().id;
36600         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36601         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36602         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36603         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36604         },
36605         
36606     getColumnRenderers : function(){
36607         var renderers = [];
36608         var cm = this.grid.colModel;
36609         var colCount = cm.getColumnCount();
36610         for(var i = 0; i < colCount; i++){
36611             renderers[i] = cm.getRenderer(i);
36612         }
36613         return renderers;
36614     },
36615     
36616     getColumnIds : function(){
36617         var ids = [];
36618         var cm = this.grid.colModel;
36619         var colCount = cm.getColumnCount();
36620         for(var i = 0; i < colCount; i++){
36621             ids[i] = cm.getColumnId(i);
36622         }
36623         return ids;
36624     },
36625     
36626     getDataIndexes : function(){
36627         if(!this.indexMap){
36628             this.indexMap = this.buildIndexMap();
36629         }
36630         return this.indexMap.colToData;
36631     },
36632     
36633     getColumnIndexByDataIndex : function(dataIndex){
36634         if(!this.indexMap){
36635             this.indexMap = this.buildIndexMap();
36636         }
36637         return this.indexMap.dataToCol[dataIndex];
36638     },
36639     
36640     /**
36641      * Set a css style for a column dynamically. 
36642      * @param {Number} colIndex The index of the column
36643      * @param {String} name The css property name
36644      * @param {String} value The css value
36645      */
36646     setCSSStyle : function(colIndex, name, value){
36647         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36648         Roo.util.CSS.updateRule(selector, name, value);
36649     },
36650     
36651     generateRules : function(cm){
36652         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36653         Roo.util.CSS.removeStyleSheet(rulesId);
36654         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36655             var cid = cm.getColumnId(i);
36656             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36657                          this.tdSelector, cid, " {\n}\n",
36658                          this.hdSelector, cid, " {\n}\n",
36659                          this.splitSelector, cid, " {\n}\n");
36660         }
36661         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36662     }
36663 });/*
36664  * Based on:
36665  * Ext JS Library 1.1.1
36666  * Copyright(c) 2006-2007, Ext JS, LLC.
36667  *
36668  * Originally Released Under LGPL - original licence link has changed is not relivant.
36669  *
36670  * Fork - LGPL
36671  * <script type="text/javascript">
36672  */
36673
36674 // private
36675 // This is a support class used internally by the Grid components
36676 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36677     this.grid = grid;
36678     this.view = grid.getView();
36679     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36680     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36681     if(hd2){
36682         this.setHandleElId(Roo.id(hd));
36683         this.setOuterHandleElId(Roo.id(hd2));
36684     }
36685     this.scroll = false;
36686 };
36687 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36688     maxDragWidth: 120,
36689     getDragData : function(e){
36690         var t = Roo.lib.Event.getTarget(e);
36691         var h = this.view.findHeaderCell(t);
36692         if(h){
36693             return {ddel: h.firstChild, header:h};
36694         }
36695         return false;
36696     },
36697
36698     onInitDrag : function(e){
36699         this.view.headersDisabled = true;
36700         var clone = this.dragData.ddel.cloneNode(true);
36701         clone.id = Roo.id();
36702         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36703         this.proxy.update(clone);
36704         return true;
36705     },
36706
36707     afterValidDrop : function(){
36708         var v = this.view;
36709         setTimeout(function(){
36710             v.headersDisabled = false;
36711         }, 50);
36712     },
36713
36714     afterInvalidDrop : function(){
36715         var v = this.view;
36716         setTimeout(function(){
36717             v.headersDisabled = false;
36718         }, 50);
36719     }
36720 });
36721 /*
36722  * Based on:
36723  * Ext JS Library 1.1.1
36724  * Copyright(c) 2006-2007, Ext JS, LLC.
36725  *
36726  * Originally Released Under LGPL - original licence link has changed is not relivant.
36727  *
36728  * Fork - LGPL
36729  * <script type="text/javascript">
36730  */
36731 // private
36732 // This is a support class used internally by the Grid components
36733 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36734     this.grid = grid;
36735     this.view = grid.getView();
36736     // split the proxies so they don't interfere with mouse events
36737     this.proxyTop = Roo.DomHelper.append(document.body, {
36738         cls:"col-move-top", html:"&#160;"
36739     }, true);
36740     this.proxyBottom = Roo.DomHelper.append(document.body, {
36741         cls:"col-move-bottom", html:"&#160;"
36742     }, true);
36743     this.proxyTop.hide = this.proxyBottom.hide = function(){
36744         this.setLeftTop(-100,-100);
36745         this.setStyle("visibility", "hidden");
36746     };
36747     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36748     // temporarily disabled
36749     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36750     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36751 };
36752 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36753     proxyOffsets : [-4, -9],
36754     fly: Roo.Element.fly,
36755
36756     getTargetFromEvent : function(e){
36757         var t = Roo.lib.Event.getTarget(e);
36758         var cindex = this.view.findCellIndex(t);
36759         if(cindex !== false){
36760             return this.view.getHeaderCell(cindex);
36761         }
36762         return null;
36763     },
36764
36765     nextVisible : function(h){
36766         var v = this.view, cm = this.grid.colModel;
36767         h = h.nextSibling;
36768         while(h){
36769             if(!cm.isHidden(v.getCellIndex(h))){
36770                 return h;
36771             }
36772             h = h.nextSibling;
36773         }
36774         return null;
36775     },
36776
36777     prevVisible : function(h){
36778         var v = this.view, cm = this.grid.colModel;
36779         h = h.prevSibling;
36780         while(h){
36781             if(!cm.isHidden(v.getCellIndex(h))){
36782                 return h;
36783             }
36784             h = h.prevSibling;
36785         }
36786         return null;
36787     },
36788
36789     positionIndicator : function(h, n, e){
36790         var x = Roo.lib.Event.getPageX(e);
36791         var r = Roo.lib.Dom.getRegion(n.firstChild);
36792         var px, pt, py = r.top + this.proxyOffsets[1];
36793         if((r.right - x) <= (r.right-r.left)/2){
36794             px = r.right+this.view.borderWidth;
36795             pt = "after";
36796         }else{
36797             px = r.left;
36798             pt = "before";
36799         }
36800         var oldIndex = this.view.getCellIndex(h);
36801         var newIndex = this.view.getCellIndex(n);
36802
36803         if(this.grid.colModel.isFixed(newIndex)){
36804             return false;
36805         }
36806
36807         var locked = this.grid.colModel.isLocked(newIndex);
36808
36809         if(pt == "after"){
36810             newIndex++;
36811         }
36812         if(oldIndex < newIndex){
36813             newIndex--;
36814         }
36815         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36816             return false;
36817         }
36818         px +=  this.proxyOffsets[0];
36819         this.proxyTop.setLeftTop(px, py);
36820         this.proxyTop.show();
36821         if(!this.bottomOffset){
36822             this.bottomOffset = this.view.mainHd.getHeight();
36823         }
36824         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36825         this.proxyBottom.show();
36826         return pt;
36827     },
36828
36829     onNodeEnter : function(n, dd, e, data){
36830         if(data.header != n){
36831             this.positionIndicator(data.header, n, e);
36832         }
36833     },
36834
36835     onNodeOver : function(n, dd, e, data){
36836         var result = false;
36837         if(data.header != n){
36838             result = this.positionIndicator(data.header, n, e);
36839         }
36840         if(!result){
36841             this.proxyTop.hide();
36842             this.proxyBottom.hide();
36843         }
36844         return result ? this.dropAllowed : this.dropNotAllowed;
36845     },
36846
36847     onNodeOut : function(n, dd, e, data){
36848         this.proxyTop.hide();
36849         this.proxyBottom.hide();
36850     },
36851
36852     onNodeDrop : function(n, dd, e, data){
36853         var h = data.header;
36854         if(h != n){
36855             var cm = this.grid.colModel;
36856             var x = Roo.lib.Event.getPageX(e);
36857             var r = Roo.lib.Dom.getRegion(n.firstChild);
36858             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36859             var oldIndex = this.view.getCellIndex(h);
36860             var newIndex = this.view.getCellIndex(n);
36861             var locked = cm.isLocked(newIndex);
36862             if(pt == "after"){
36863                 newIndex++;
36864             }
36865             if(oldIndex < newIndex){
36866                 newIndex--;
36867             }
36868             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36869                 return false;
36870             }
36871             cm.setLocked(oldIndex, locked, true);
36872             cm.moveColumn(oldIndex, newIndex);
36873             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36874             return true;
36875         }
36876         return false;
36877     }
36878 });
36879 /*
36880  * Based on:
36881  * Ext JS Library 1.1.1
36882  * Copyright(c) 2006-2007, Ext JS, LLC.
36883  *
36884  * Originally Released Under LGPL - original licence link has changed is not relivant.
36885  *
36886  * Fork - LGPL
36887  * <script type="text/javascript">
36888  */
36889   
36890 /**
36891  * @class Roo.grid.GridView
36892  * @extends Roo.util.Observable
36893  *
36894  * @constructor
36895  * @param {Object} config
36896  */
36897 Roo.grid.GridView = function(config){
36898     Roo.grid.GridView.superclass.constructor.call(this);
36899     this.el = null;
36900
36901     Roo.apply(this, config);
36902 };
36903
36904 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36905
36906     unselectable :  'unselectable="on"',
36907     unselectableCls :  'x-unselectable',
36908     
36909     
36910     rowClass : "x-grid-row",
36911
36912     cellClass : "x-grid-col",
36913
36914     tdClass : "x-grid-td",
36915
36916     hdClass : "x-grid-hd",
36917
36918     splitClass : "x-grid-split",
36919
36920     sortClasses : ["sort-asc", "sort-desc"],
36921
36922     enableMoveAnim : false,
36923
36924     hlColor: "C3DAF9",
36925
36926     dh : Roo.DomHelper,
36927
36928     fly : Roo.Element.fly,
36929
36930     css : Roo.util.CSS,
36931
36932     borderWidth: 1,
36933
36934     splitOffset: 3,
36935
36936     scrollIncrement : 22,
36937
36938     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36939
36940     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36941
36942     bind : function(ds, cm){
36943         if(this.ds){
36944             this.ds.un("load", this.onLoad, this);
36945             this.ds.un("datachanged", this.onDataChange, this);
36946             this.ds.un("add", this.onAdd, this);
36947             this.ds.un("remove", this.onRemove, this);
36948             this.ds.un("update", this.onUpdate, this);
36949             this.ds.un("clear", this.onClear, this);
36950         }
36951         if(ds){
36952             ds.on("load", this.onLoad, this);
36953             ds.on("datachanged", this.onDataChange, this);
36954             ds.on("add", this.onAdd, this);
36955             ds.on("remove", this.onRemove, this);
36956             ds.on("update", this.onUpdate, this);
36957             ds.on("clear", this.onClear, this);
36958         }
36959         this.ds = ds;
36960
36961         if(this.cm){
36962             this.cm.un("widthchange", this.onColWidthChange, this);
36963             this.cm.un("headerchange", this.onHeaderChange, this);
36964             this.cm.un("hiddenchange", this.onHiddenChange, this);
36965             this.cm.un("columnmoved", this.onColumnMove, this);
36966             this.cm.un("columnlockchange", this.onColumnLock, this);
36967         }
36968         if(cm){
36969             this.generateRules(cm);
36970             cm.on("widthchange", this.onColWidthChange, this);
36971             cm.on("headerchange", this.onHeaderChange, this);
36972             cm.on("hiddenchange", this.onHiddenChange, this);
36973             cm.on("columnmoved", this.onColumnMove, this);
36974             cm.on("columnlockchange", this.onColumnLock, this);
36975         }
36976         this.cm = cm;
36977     },
36978
36979     init: function(grid){
36980         Roo.grid.GridView.superclass.init.call(this, grid);
36981
36982         this.bind(grid.dataSource, grid.colModel);
36983
36984         grid.on("headerclick", this.handleHeaderClick, this);
36985
36986         if(grid.trackMouseOver){
36987             grid.on("mouseover", this.onRowOver, this);
36988             grid.on("mouseout", this.onRowOut, this);
36989         }
36990         grid.cancelTextSelection = function(){};
36991         this.gridId = grid.id;
36992
36993         var tpls = this.templates || {};
36994
36995         if(!tpls.master){
36996             tpls.master = new Roo.Template(
36997                '<div class="x-grid" hidefocus="true">',
36998                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36999                   '<div class="x-grid-topbar"></div>',
37000                   '<div class="x-grid-scroller"><div></div></div>',
37001                   '<div class="x-grid-locked">',
37002                       '<div class="x-grid-header">{lockedHeader}</div>',
37003                       '<div class="x-grid-body">{lockedBody}</div>',
37004                   "</div>",
37005                   '<div class="x-grid-viewport">',
37006                       '<div class="x-grid-header">{header}</div>',
37007                       '<div class="x-grid-body">{body}</div>',
37008                   "</div>",
37009                   '<div class="x-grid-bottombar"></div>',
37010                  
37011                   '<div class="x-grid-resize-proxy">&#160;</div>',
37012                "</div>"
37013             );
37014             tpls.master.disableformats = true;
37015         }
37016
37017         if(!tpls.header){
37018             tpls.header = new Roo.Template(
37019                '<table border="0" cellspacing="0" cellpadding="0">',
37020                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37021                "</table>{splits}"
37022             );
37023             tpls.header.disableformats = true;
37024         }
37025         tpls.header.compile();
37026
37027         if(!tpls.hcell){
37028             tpls.hcell = new Roo.Template(
37029                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37030                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37031                 "</div></td>"
37032              );
37033              tpls.hcell.disableFormats = true;
37034         }
37035         tpls.hcell.compile();
37036
37037         if(!tpls.hsplit){
37038             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37039                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37040             tpls.hsplit.disableFormats = true;
37041         }
37042         tpls.hsplit.compile();
37043
37044         if(!tpls.body){
37045             tpls.body = new Roo.Template(
37046                '<table border="0" cellspacing="0" cellpadding="0">',
37047                "<tbody>{rows}</tbody>",
37048                "</table>"
37049             );
37050             tpls.body.disableFormats = true;
37051         }
37052         tpls.body.compile();
37053
37054         if(!tpls.row){
37055             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37056             tpls.row.disableFormats = true;
37057         }
37058         tpls.row.compile();
37059
37060         if(!tpls.cell){
37061             tpls.cell = new Roo.Template(
37062                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37063                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37064                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37065                 "</td>"
37066             );
37067             tpls.cell.disableFormats = true;
37068         }
37069         tpls.cell.compile();
37070
37071         this.templates = tpls;
37072     },
37073
37074     // remap these for backwards compat
37075     onColWidthChange : function(){
37076         this.updateColumns.apply(this, arguments);
37077     },
37078     onHeaderChange : function(){
37079         this.updateHeaders.apply(this, arguments);
37080     }, 
37081     onHiddenChange : function(){
37082         this.handleHiddenChange.apply(this, arguments);
37083     },
37084     onColumnMove : function(){
37085         this.handleColumnMove.apply(this, arguments);
37086     },
37087     onColumnLock : function(){
37088         this.handleLockChange.apply(this, arguments);
37089     },
37090
37091     onDataChange : function(){
37092         this.refresh();
37093         this.updateHeaderSortState();
37094     },
37095
37096     onClear : function(){
37097         this.refresh();
37098     },
37099
37100     onUpdate : function(ds, record){
37101         this.refreshRow(record);
37102     },
37103
37104     refreshRow : function(record){
37105         var ds = this.ds, index;
37106         if(typeof record == 'number'){
37107             index = record;
37108             record = ds.getAt(index);
37109         }else{
37110             index = ds.indexOf(record);
37111         }
37112         this.insertRows(ds, index, index, true);
37113         this.onRemove(ds, record, index+1, true);
37114         this.syncRowHeights(index, index);
37115         this.layout();
37116         this.fireEvent("rowupdated", this, index, record);
37117     },
37118
37119     onAdd : function(ds, records, index){
37120         this.insertRows(ds, index, index + (records.length-1));
37121     },
37122
37123     onRemove : function(ds, record, index, isUpdate){
37124         if(isUpdate !== true){
37125             this.fireEvent("beforerowremoved", this, index, record);
37126         }
37127         var bt = this.getBodyTable(), lt = this.getLockedTable();
37128         if(bt.rows[index]){
37129             bt.firstChild.removeChild(bt.rows[index]);
37130         }
37131         if(lt.rows[index]){
37132             lt.firstChild.removeChild(lt.rows[index]);
37133         }
37134         if(isUpdate !== true){
37135             this.stripeRows(index);
37136             this.syncRowHeights(index, index);
37137             this.layout();
37138             this.fireEvent("rowremoved", this, index, record);
37139         }
37140     },
37141
37142     onLoad : function(){
37143         this.scrollToTop();
37144     },
37145
37146     /**
37147      * Scrolls the grid to the top
37148      */
37149     scrollToTop : function(){
37150         if(this.scroller){
37151             this.scroller.dom.scrollTop = 0;
37152             this.syncScroll();
37153         }
37154     },
37155
37156     /**
37157      * Gets a panel in the header of the grid that can be used for toolbars etc.
37158      * After modifying the contents of this panel a call to grid.autoSize() may be
37159      * required to register any changes in size.
37160      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37161      * @return Roo.Element
37162      */
37163     getHeaderPanel : function(doShow){
37164         if(doShow){
37165             this.headerPanel.show();
37166         }
37167         return this.headerPanel;
37168     },
37169
37170     /**
37171      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37172      * After modifying the contents of this panel a call to grid.autoSize() may be
37173      * required to register any changes in size.
37174      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37175      * @return Roo.Element
37176      */
37177     getFooterPanel : function(doShow){
37178         if(doShow){
37179             this.footerPanel.show();
37180         }
37181         return this.footerPanel;
37182     },
37183
37184     initElements : function(){
37185         var E = Roo.Element;
37186         var el = this.grid.getGridEl().dom.firstChild;
37187         var cs = el.childNodes;
37188
37189         this.el = new E(el);
37190         
37191          this.focusEl = new E(el.firstChild);
37192         this.focusEl.swallowEvent("click", true);
37193         
37194         this.headerPanel = new E(cs[1]);
37195         this.headerPanel.enableDisplayMode("block");
37196
37197         this.scroller = new E(cs[2]);
37198         this.scrollSizer = new E(this.scroller.dom.firstChild);
37199
37200         this.lockedWrap = new E(cs[3]);
37201         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37202         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37203
37204         this.mainWrap = new E(cs[4]);
37205         this.mainHd = new E(this.mainWrap.dom.firstChild);
37206         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37207
37208         this.footerPanel = new E(cs[5]);
37209         this.footerPanel.enableDisplayMode("block");
37210
37211         this.resizeProxy = new E(cs[6]);
37212
37213         this.headerSelector = String.format(
37214            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37215            this.lockedHd.id, this.mainHd.id
37216         );
37217
37218         this.splitterSelector = String.format(
37219            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37220            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37221         );
37222     },
37223     idToCssName : function(s)
37224     {
37225         return s.replace(/[^a-z0-9]+/ig, '-');
37226     },
37227
37228     getHeaderCell : function(index){
37229         return Roo.DomQuery.select(this.headerSelector)[index];
37230     },
37231
37232     getHeaderCellMeasure : function(index){
37233         return this.getHeaderCell(index).firstChild;
37234     },
37235
37236     getHeaderCellText : function(index){
37237         return this.getHeaderCell(index).firstChild.firstChild;
37238     },
37239
37240     getLockedTable : function(){
37241         return this.lockedBody.dom.firstChild;
37242     },
37243
37244     getBodyTable : function(){
37245         return this.mainBody.dom.firstChild;
37246     },
37247
37248     getLockedRow : function(index){
37249         return this.getLockedTable().rows[index];
37250     },
37251
37252     getRow : function(index){
37253         return this.getBodyTable().rows[index];
37254     },
37255
37256     getRowComposite : function(index){
37257         if(!this.rowEl){
37258             this.rowEl = new Roo.CompositeElementLite();
37259         }
37260         var els = [], lrow, mrow;
37261         if(lrow = this.getLockedRow(index)){
37262             els.push(lrow);
37263         }
37264         if(mrow = this.getRow(index)){
37265             els.push(mrow);
37266         }
37267         this.rowEl.elements = els;
37268         return this.rowEl;
37269     },
37270     /**
37271      * Gets the 'td' of the cell
37272      * 
37273      * @param {Integer} rowIndex row to select
37274      * @param {Integer} colIndex column to select
37275      * 
37276      * @return {Object} 
37277      */
37278     getCell : function(rowIndex, colIndex){
37279         var locked = this.cm.getLockedCount();
37280         var source;
37281         if(colIndex < locked){
37282             source = this.lockedBody.dom.firstChild;
37283         }else{
37284             source = this.mainBody.dom.firstChild;
37285             colIndex -= locked;
37286         }
37287         return source.rows[rowIndex].childNodes[colIndex];
37288     },
37289
37290     getCellText : function(rowIndex, colIndex){
37291         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37292     },
37293
37294     getCellBox : function(cell){
37295         var b = this.fly(cell).getBox();
37296         if(Roo.isOpera){ // opera fails to report the Y
37297             b.y = cell.offsetTop + this.mainBody.getY();
37298         }
37299         return b;
37300     },
37301
37302     getCellIndex : function(cell){
37303         var id = String(cell.className).match(this.cellRE);
37304         if(id){
37305             return parseInt(id[1], 10);
37306         }
37307         return 0;
37308     },
37309
37310     findHeaderIndex : function(n){
37311         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37312         return r ? this.getCellIndex(r) : false;
37313     },
37314
37315     findHeaderCell : function(n){
37316         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37317         return r ? r : false;
37318     },
37319
37320     findRowIndex : function(n){
37321         if(!n){
37322             return false;
37323         }
37324         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37325         return r ? r.rowIndex : false;
37326     },
37327
37328     findCellIndex : function(node){
37329         var stop = this.el.dom;
37330         while(node && node != stop){
37331             if(this.findRE.test(node.className)){
37332                 return this.getCellIndex(node);
37333             }
37334             node = node.parentNode;
37335         }
37336         return false;
37337     },
37338
37339     getColumnId : function(index){
37340         return this.cm.getColumnId(index);
37341     },
37342
37343     getSplitters : function()
37344     {
37345         if(this.splitterSelector){
37346            return Roo.DomQuery.select(this.splitterSelector);
37347         }else{
37348             return null;
37349       }
37350     },
37351
37352     getSplitter : function(index){
37353         return this.getSplitters()[index];
37354     },
37355
37356     onRowOver : function(e, t){
37357         var row;
37358         if((row = this.findRowIndex(t)) !== false){
37359             this.getRowComposite(row).addClass("x-grid-row-over");
37360         }
37361     },
37362
37363     onRowOut : function(e, t){
37364         var row;
37365         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37366             this.getRowComposite(row).removeClass("x-grid-row-over");
37367         }
37368     },
37369
37370     renderHeaders : function(){
37371         var cm = this.cm;
37372         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37373         var cb = [], lb = [], sb = [], lsb = [], p = {};
37374         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37375             p.cellId = "x-grid-hd-0-" + i;
37376             p.splitId = "x-grid-csplit-0-" + i;
37377             p.id = cm.getColumnId(i);
37378             p.title = cm.getColumnTooltip(i) || "";
37379             p.value = cm.getColumnHeader(i) || "";
37380             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37381             if(!cm.isLocked(i)){
37382                 cb[cb.length] = ct.apply(p);
37383                 sb[sb.length] = st.apply(p);
37384             }else{
37385                 lb[lb.length] = ct.apply(p);
37386                 lsb[lsb.length] = st.apply(p);
37387             }
37388         }
37389         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37390                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37391     },
37392
37393     updateHeaders : function(){
37394         var html = this.renderHeaders();
37395         this.lockedHd.update(html[0]);
37396         this.mainHd.update(html[1]);
37397     },
37398
37399     /**
37400      * Focuses the specified row.
37401      * @param {Number} row The row index
37402      */
37403     focusRow : function(row)
37404     {
37405         //Roo.log('GridView.focusRow');
37406         var x = this.scroller.dom.scrollLeft;
37407         this.focusCell(row, 0, false);
37408         this.scroller.dom.scrollLeft = x;
37409     },
37410
37411     /**
37412      * Focuses the specified cell.
37413      * @param {Number} row The row index
37414      * @param {Number} col The column index
37415      * @param {Boolean} hscroll false to disable horizontal scrolling
37416      */
37417     focusCell : function(row, col, hscroll)
37418     {
37419         //Roo.log('GridView.focusCell');
37420         var el = this.ensureVisible(row, col, hscroll);
37421         this.focusEl.alignTo(el, "tl-tl");
37422         if(Roo.isGecko){
37423             this.focusEl.focus();
37424         }else{
37425             this.focusEl.focus.defer(1, this.focusEl);
37426         }
37427     },
37428
37429     /**
37430      * Scrolls the specified cell into view
37431      * @param {Number} row The row index
37432      * @param {Number} col The column index
37433      * @param {Boolean} hscroll false to disable horizontal scrolling
37434      */
37435     ensureVisible : function(row, col, hscroll)
37436     {
37437         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37438         //return null; //disable for testing.
37439         if(typeof row != "number"){
37440             row = row.rowIndex;
37441         }
37442         if(row < 0 && row >= this.ds.getCount()){
37443             return  null;
37444         }
37445         col = (col !== undefined ? col : 0);
37446         var cm = this.grid.colModel;
37447         while(cm.isHidden(col)){
37448             col++;
37449         }
37450
37451         var el = this.getCell(row, col);
37452         if(!el){
37453             return null;
37454         }
37455         var c = this.scroller.dom;
37456
37457         var ctop = parseInt(el.offsetTop, 10);
37458         var cleft = parseInt(el.offsetLeft, 10);
37459         var cbot = ctop + el.offsetHeight;
37460         var cright = cleft + el.offsetWidth;
37461         
37462         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37463         var stop = parseInt(c.scrollTop, 10);
37464         var sleft = parseInt(c.scrollLeft, 10);
37465         var sbot = stop + ch;
37466         var sright = sleft + c.clientWidth;
37467         /*
37468         Roo.log('GridView.ensureVisible:' +
37469                 ' ctop:' + ctop +
37470                 ' c.clientHeight:' + c.clientHeight +
37471                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37472                 ' stop:' + stop +
37473                 ' cbot:' + cbot +
37474                 ' sbot:' + sbot +
37475                 ' ch:' + ch  
37476                 );
37477         */
37478         if(ctop < stop){
37479              c.scrollTop = ctop;
37480             //Roo.log("set scrolltop to ctop DISABLE?");
37481         }else if(cbot > sbot){
37482             //Roo.log("set scrolltop to cbot-ch");
37483             c.scrollTop = cbot-ch;
37484         }
37485         
37486         if(hscroll !== false){
37487             if(cleft < sleft){
37488                 c.scrollLeft = cleft;
37489             }else if(cright > sright){
37490                 c.scrollLeft = cright-c.clientWidth;
37491             }
37492         }
37493          
37494         return el;
37495     },
37496
37497     updateColumns : function(){
37498         this.grid.stopEditing();
37499         var cm = this.grid.colModel, colIds = this.getColumnIds();
37500         //var totalWidth = cm.getTotalWidth();
37501         var pos = 0;
37502         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37503             //if(cm.isHidden(i)) continue;
37504             var w = cm.getColumnWidth(i);
37505             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37506             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37507         }
37508         this.updateSplitters();
37509     },
37510
37511     generateRules : function(cm){
37512         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37513         Roo.util.CSS.removeStyleSheet(rulesId);
37514         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37515             var cid = cm.getColumnId(i);
37516             var align = '';
37517             if(cm.config[i].align){
37518                 align = 'text-align:'+cm.config[i].align+';';
37519             }
37520             var hidden = '';
37521             if(cm.isHidden(i)){
37522                 hidden = 'display:none;';
37523             }
37524             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37525             ruleBuf.push(
37526                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37527                     this.hdSelector, cid, " {\n", align, width, "}\n",
37528                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37529                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37530         }
37531         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37532     },
37533
37534     updateSplitters : function(){
37535         var cm = this.cm, s = this.getSplitters();
37536         if(s){ // splitters not created yet
37537             var pos = 0, locked = true;
37538             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37539                 if(cm.isHidden(i)) continue;
37540                 var w = cm.getColumnWidth(i); // make sure it's a number
37541                 if(!cm.isLocked(i) && locked){
37542                     pos = 0;
37543                     locked = false;
37544                 }
37545                 pos += w;
37546                 s[i].style.left = (pos-this.splitOffset) + "px";
37547             }
37548         }
37549     },
37550
37551     handleHiddenChange : function(colModel, colIndex, hidden){
37552         if(hidden){
37553             this.hideColumn(colIndex);
37554         }else{
37555             this.unhideColumn(colIndex);
37556         }
37557     },
37558
37559     hideColumn : function(colIndex){
37560         var cid = this.getColumnId(colIndex);
37561         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37562         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37563         if(Roo.isSafari){
37564             this.updateHeaders();
37565         }
37566         this.updateSplitters();
37567         this.layout();
37568     },
37569
37570     unhideColumn : function(colIndex){
37571         var cid = this.getColumnId(colIndex);
37572         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37573         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37574
37575         if(Roo.isSafari){
37576             this.updateHeaders();
37577         }
37578         this.updateSplitters();
37579         this.layout();
37580     },
37581
37582     insertRows : function(dm, firstRow, lastRow, isUpdate){
37583         if(firstRow == 0 && lastRow == dm.getCount()-1){
37584             this.refresh();
37585         }else{
37586             if(!isUpdate){
37587                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37588             }
37589             var s = this.getScrollState();
37590             var markup = this.renderRows(firstRow, lastRow);
37591             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37592             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37593             this.restoreScroll(s);
37594             if(!isUpdate){
37595                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37596                 this.syncRowHeights(firstRow, lastRow);
37597                 this.stripeRows(firstRow);
37598                 this.layout();
37599             }
37600         }
37601     },
37602
37603     bufferRows : function(markup, target, index){
37604         var before = null, trows = target.rows, tbody = target.tBodies[0];
37605         if(index < trows.length){
37606             before = trows[index];
37607         }
37608         var b = document.createElement("div");
37609         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37610         var rows = b.firstChild.rows;
37611         for(var i = 0, len = rows.length; i < len; i++){
37612             if(before){
37613                 tbody.insertBefore(rows[0], before);
37614             }else{
37615                 tbody.appendChild(rows[0]);
37616             }
37617         }
37618         b.innerHTML = "";
37619         b = null;
37620     },
37621
37622     deleteRows : function(dm, firstRow, lastRow){
37623         if(dm.getRowCount()<1){
37624             this.fireEvent("beforerefresh", this);
37625             this.mainBody.update("");
37626             this.lockedBody.update("");
37627             this.fireEvent("refresh", this);
37628         }else{
37629             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37630             var bt = this.getBodyTable();
37631             var tbody = bt.firstChild;
37632             var rows = bt.rows;
37633             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37634                 tbody.removeChild(rows[firstRow]);
37635             }
37636             this.stripeRows(firstRow);
37637             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37638         }
37639     },
37640
37641     updateRows : function(dataSource, firstRow, lastRow){
37642         var s = this.getScrollState();
37643         this.refresh();
37644         this.restoreScroll(s);
37645     },
37646
37647     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37648         if(!noRefresh){
37649            this.refresh();
37650         }
37651         this.updateHeaderSortState();
37652     },
37653
37654     getScrollState : function(){
37655         
37656         var sb = this.scroller.dom;
37657         return {left: sb.scrollLeft, top: sb.scrollTop};
37658     },
37659
37660     stripeRows : function(startRow){
37661         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37662             return;
37663         }
37664         startRow = startRow || 0;
37665         var rows = this.getBodyTable().rows;
37666         var lrows = this.getLockedTable().rows;
37667         var cls = ' x-grid-row-alt ';
37668         for(var i = startRow, len = rows.length; i < len; i++){
37669             var row = rows[i], lrow = lrows[i];
37670             var isAlt = ((i+1) % 2 == 0);
37671             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37672             if(isAlt == hasAlt){
37673                 continue;
37674             }
37675             if(isAlt){
37676                 row.className += " x-grid-row-alt";
37677             }else{
37678                 row.className = row.className.replace("x-grid-row-alt", "");
37679             }
37680             if(lrow){
37681                 lrow.className = row.className;
37682             }
37683         }
37684     },
37685
37686     restoreScroll : function(state){
37687         //Roo.log('GridView.restoreScroll');
37688         var sb = this.scroller.dom;
37689         sb.scrollLeft = state.left;
37690         sb.scrollTop = state.top;
37691         this.syncScroll();
37692     },
37693
37694     syncScroll : function(){
37695         //Roo.log('GridView.syncScroll');
37696         var sb = this.scroller.dom;
37697         var sh = this.mainHd.dom;
37698         var bs = this.mainBody.dom;
37699         var lv = this.lockedBody.dom;
37700         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37701         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37702     },
37703
37704     handleScroll : function(e){
37705         this.syncScroll();
37706         var sb = this.scroller.dom;
37707         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37708         e.stopEvent();
37709     },
37710
37711     handleWheel : function(e){
37712         var d = e.getWheelDelta();
37713         this.scroller.dom.scrollTop -= d*22;
37714         // set this here to prevent jumpy scrolling on large tables
37715         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37716         e.stopEvent();
37717     },
37718
37719     renderRows : function(startRow, endRow){
37720         // pull in all the crap needed to render rows
37721         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37722         var colCount = cm.getColumnCount();
37723
37724         if(ds.getCount() < 1){
37725             return ["", ""];
37726         }
37727
37728         // build a map for all the columns
37729         var cs = [];
37730         for(var i = 0; i < colCount; i++){
37731             var name = cm.getDataIndex(i);
37732             cs[i] = {
37733                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37734                 renderer : cm.getRenderer(i),
37735                 id : cm.getColumnId(i),
37736                 locked : cm.isLocked(i)
37737             };
37738         }
37739
37740         startRow = startRow || 0;
37741         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37742
37743         // records to render
37744         var rs = ds.getRange(startRow, endRow);
37745
37746         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37747     },
37748
37749     // As much as I hate to duplicate code, this was branched because FireFox really hates
37750     // [].join("") on strings. The performance difference was substantial enough to
37751     // branch this function
37752     doRender : Roo.isGecko ?
37753             function(cs, rs, ds, startRow, colCount, stripe){
37754                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37755                 // buffers
37756                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37757                 
37758                 var hasListener = this.grid.hasListener('rowclass');
37759                 var rowcfg = {};
37760                 for(var j = 0, len = rs.length; j < len; j++){
37761                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37762                     for(var i = 0; i < colCount; i++){
37763                         c = cs[i];
37764                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37765                         p.id = c.id;
37766                         p.css = p.attr = "";
37767                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37768                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37769                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37770                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37771                         }
37772                         var markup = ct.apply(p);
37773                         if(!c.locked){
37774                             cb+= markup;
37775                         }else{
37776                             lcb+= markup;
37777                         }
37778                     }
37779                     var alt = [];
37780                     if(stripe && ((rowIndex+1) % 2 == 0)){
37781                         alt.push("x-grid-row-alt")
37782                     }
37783                     if(r.dirty){
37784                         alt.push(  " x-grid-dirty-row");
37785                     }
37786                     rp.cells = lcb;
37787                     if(this.getRowClass){
37788                         alt.push(this.getRowClass(r, rowIndex));
37789                     }
37790                     if (hasListener) {
37791                         rowcfg = {
37792                              
37793                             record: r,
37794                             rowIndex : rowIndex,
37795                             rowClass : ''
37796                         }
37797                         this.grid.fireEvent('rowclass', this, rowcfg);
37798                         alt.push(rowcfg.rowClass);
37799                     }
37800                     rp.alt = alt.join(" ");
37801                     lbuf+= rt.apply(rp);
37802                     rp.cells = cb;
37803                     buf+=  rt.apply(rp);
37804                 }
37805                 return [lbuf, buf];
37806             } :
37807             function(cs, rs, ds, startRow, colCount, stripe){
37808                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37809                 // buffers
37810                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37811                 var hasListener = this.grid.hasListener('rowclass');
37812  
37813                 var rowcfg = {};
37814                 for(var j = 0, len = rs.length; j < len; j++){
37815                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37816                     for(var i = 0; i < colCount; i++){
37817                         c = cs[i];
37818                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37819                         p.id = c.id;
37820                         p.css = p.attr = "";
37821                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37822                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37823                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37824                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37825                         }
37826                         
37827                         var markup = ct.apply(p);
37828                         if(!c.locked){
37829                             cb[cb.length] = markup;
37830                         }else{
37831                             lcb[lcb.length] = markup;
37832                         }
37833                     }
37834                     var alt = [];
37835                     if(stripe && ((rowIndex+1) % 2 == 0)){
37836                         alt.push( "x-grid-row-alt");
37837                     }
37838                     if(r.dirty){
37839                         alt.push(" x-grid-dirty-row");
37840                     }
37841                     rp.cells = lcb;
37842                     if(this.getRowClass){
37843                         alt.push( this.getRowClass(r, rowIndex));
37844                     }
37845                     if (hasListener) {
37846                         rowcfg = {
37847                              
37848                             record: r,
37849                             rowIndex : rowIndex,
37850                             rowClass : ''
37851                         }
37852                         this.grid.fireEvent('rowclass', this, rowcfg);
37853                         alt.push(rowcfg.rowClass);
37854                     }
37855                     rp.alt = alt.join(" ");
37856                     rp.cells = lcb.join("");
37857                     lbuf[lbuf.length] = rt.apply(rp);
37858                     rp.cells = cb.join("");
37859                     buf[buf.length] =  rt.apply(rp);
37860                 }
37861                 return [lbuf.join(""), buf.join("")];
37862             },
37863
37864     renderBody : function(){
37865         var markup = this.renderRows();
37866         var bt = this.templates.body;
37867         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37868     },
37869
37870     /**
37871      * Refreshes the grid
37872      * @param {Boolean} headersToo
37873      */
37874     refresh : function(headersToo){
37875         this.fireEvent("beforerefresh", this);
37876         this.grid.stopEditing();
37877         var result = this.renderBody();
37878         this.lockedBody.update(result[0]);
37879         this.mainBody.update(result[1]);
37880         if(headersToo === true){
37881             this.updateHeaders();
37882             this.updateColumns();
37883             this.updateSplitters();
37884             this.updateHeaderSortState();
37885         }
37886         this.syncRowHeights();
37887         this.layout();
37888         this.fireEvent("refresh", this);
37889     },
37890
37891     handleColumnMove : function(cm, oldIndex, newIndex){
37892         this.indexMap = null;
37893         var s = this.getScrollState();
37894         this.refresh(true);
37895         this.restoreScroll(s);
37896         this.afterMove(newIndex);
37897     },
37898
37899     afterMove : function(colIndex){
37900         if(this.enableMoveAnim && Roo.enableFx){
37901             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37902         }
37903         // if multisort - fix sortOrder, and reload..
37904         if (this.grid.dataSource.multiSort) {
37905             // the we can call sort again..
37906             var dm = this.grid.dataSource;
37907             var cm = this.grid.colModel;
37908             var so = [];
37909             for(var i = 0; i < cm.config.length; i++ ) {
37910                 
37911                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37912                     continue; // dont' bother, it's not in sort list or being set.
37913                 }
37914                 
37915                 so.push(cm.config[i].dataIndex);
37916             };
37917             dm.sortOrder = so;
37918             dm.load(dm.lastOptions);
37919             
37920             
37921         }
37922         
37923     },
37924
37925     updateCell : function(dm, rowIndex, dataIndex){
37926         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37927         if(typeof colIndex == "undefined"){ // not present in grid
37928             return;
37929         }
37930         var cm = this.grid.colModel;
37931         var cell = this.getCell(rowIndex, colIndex);
37932         var cellText = this.getCellText(rowIndex, colIndex);
37933
37934         var p = {
37935             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37936             id : cm.getColumnId(colIndex),
37937             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37938         };
37939         var renderer = cm.getRenderer(colIndex);
37940         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37941         if(typeof val == "undefined" || val === "") val = "&#160;";
37942         cellText.innerHTML = val;
37943         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37944         this.syncRowHeights(rowIndex, rowIndex);
37945     },
37946
37947     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37948         var maxWidth = 0;
37949         if(this.grid.autoSizeHeaders){
37950             var h = this.getHeaderCellMeasure(colIndex);
37951             maxWidth = Math.max(maxWidth, h.scrollWidth);
37952         }
37953         var tb, index;
37954         if(this.cm.isLocked(colIndex)){
37955             tb = this.getLockedTable();
37956             index = colIndex;
37957         }else{
37958             tb = this.getBodyTable();
37959             index = colIndex - this.cm.getLockedCount();
37960         }
37961         if(tb && tb.rows){
37962             var rows = tb.rows;
37963             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37964             for(var i = 0; i < stopIndex; i++){
37965                 var cell = rows[i].childNodes[index].firstChild;
37966                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37967             }
37968         }
37969         return maxWidth + /*margin for error in IE*/ 5;
37970     },
37971     /**
37972      * Autofit a column to its content.
37973      * @param {Number} colIndex
37974      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37975      */
37976      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37977          if(this.cm.isHidden(colIndex)){
37978              return; // can't calc a hidden column
37979          }
37980         if(forceMinSize){
37981             var cid = this.cm.getColumnId(colIndex);
37982             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37983            if(this.grid.autoSizeHeaders){
37984                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37985            }
37986         }
37987         var newWidth = this.calcColumnWidth(colIndex);
37988         this.cm.setColumnWidth(colIndex,
37989             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37990         if(!suppressEvent){
37991             this.grid.fireEvent("columnresize", colIndex, newWidth);
37992         }
37993     },
37994
37995     /**
37996      * Autofits all columns to their content and then expands to fit any extra space in the grid
37997      */
37998      autoSizeColumns : function(){
37999         var cm = this.grid.colModel;
38000         var colCount = cm.getColumnCount();
38001         for(var i = 0; i < colCount; i++){
38002             this.autoSizeColumn(i, true, true);
38003         }
38004         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38005             this.fitColumns();
38006         }else{
38007             this.updateColumns();
38008             this.layout();
38009         }
38010     },
38011
38012     /**
38013      * Autofits all columns to the grid's width proportionate with their current size
38014      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38015      */
38016     fitColumns : function(reserveScrollSpace){
38017         var cm = this.grid.colModel;
38018         var colCount = cm.getColumnCount();
38019         var cols = [];
38020         var width = 0;
38021         var i, w;
38022         for (i = 0; i < colCount; i++){
38023             if(!cm.isHidden(i) && !cm.isFixed(i)){
38024                 w = cm.getColumnWidth(i);
38025                 cols.push(i);
38026                 cols.push(w);
38027                 width += w;
38028             }
38029         }
38030         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38031         if(reserveScrollSpace){
38032             avail -= 17;
38033         }
38034         var frac = (avail - cm.getTotalWidth())/width;
38035         while (cols.length){
38036             w = cols.pop();
38037             i = cols.pop();
38038             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38039         }
38040         this.updateColumns();
38041         this.layout();
38042     },
38043
38044     onRowSelect : function(rowIndex){
38045         var row = this.getRowComposite(rowIndex);
38046         row.addClass("x-grid-row-selected");
38047     },
38048
38049     onRowDeselect : function(rowIndex){
38050         var row = this.getRowComposite(rowIndex);
38051         row.removeClass("x-grid-row-selected");
38052     },
38053
38054     onCellSelect : function(row, col){
38055         var cell = this.getCell(row, col);
38056         if(cell){
38057             Roo.fly(cell).addClass("x-grid-cell-selected");
38058         }
38059     },
38060
38061     onCellDeselect : function(row, col){
38062         var cell = this.getCell(row, col);
38063         if(cell){
38064             Roo.fly(cell).removeClass("x-grid-cell-selected");
38065         }
38066     },
38067
38068     updateHeaderSortState : function(){
38069         
38070         // sort state can be single { field: xxx, direction : yyy}
38071         // or   { xxx=>ASC , yyy : DESC ..... }
38072         
38073         var mstate = {};
38074         if (!this.ds.multiSort) { 
38075             var state = this.ds.getSortState();
38076             if(!state){
38077                 return;
38078             }
38079             mstate[state.field] = state.direction;
38080             // FIXME... - this is not used here.. but might be elsewhere..
38081             this.sortState = state;
38082             
38083         } else {
38084             mstate = this.ds.sortToggle;
38085         }
38086         //remove existing sort classes..
38087         
38088         var sc = this.sortClasses;
38089         var hds = this.el.select(this.headerSelector).removeClass(sc);
38090         
38091         for(var f in mstate) {
38092         
38093             var sortColumn = this.cm.findColumnIndex(f);
38094             
38095             if(sortColumn != -1){
38096                 var sortDir = mstate[f];        
38097                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38098             }
38099         }
38100         
38101          
38102         
38103     },
38104
38105
38106     handleHeaderClick : function(g, index,e){
38107         
38108         Roo.log("header click");
38109         
38110         if (Roo.isTouch) {
38111             // touch events on header are handled by context
38112             this.handleHdCtx(g,index,e);
38113             return;
38114         }
38115         
38116         
38117         if(this.headersDisabled){
38118             return;
38119         }
38120         var dm = g.dataSource, cm = g.colModel;
38121         if(!cm.isSortable(index)){
38122             return;
38123         }
38124         g.stopEditing();
38125         
38126         if (dm.multiSort) {
38127             // update the sortOrder
38128             var so = [];
38129             for(var i = 0; i < cm.config.length; i++ ) {
38130                 
38131                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38132                     continue; // dont' bother, it's not in sort list or being set.
38133                 }
38134                 
38135                 so.push(cm.config[i].dataIndex);
38136             };
38137             dm.sortOrder = so;
38138         }
38139         
38140         
38141         dm.sort(cm.getDataIndex(index));
38142     },
38143
38144
38145     destroy : function(){
38146         if(this.colMenu){
38147             this.colMenu.removeAll();
38148             Roo.menu.MenuMgr.unregister(this.colMenu);
38149             this.colMenu.getEl().remove();
38150             delete this.colMenu;
38151         }
38152         if(this.hmenu){
38153             this.hmenu.removeAll();
38154             Roo.menu.MenuMgr.unregister(this.hmenu);
38155             this.hmenu.getEl().remove();
38156             delete this.hmenu;
38157         }
38158         if(this.grid.enableColumnMove){
38159             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38160             if(dds){
38161                 for(var dd in dds){
38162                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38163                         var elid = dds[dd].dragElId;
38164                         dds[dd].unreg();
38165                         Roo.get(elid).remove();
38166                     } else if(dds[dd].config.isTarget){
38167                         dds[dd].proxyTop.remove();
38168                         dds[dd].proxyBottom.remove();
38169                         dds[dd].unreg();
38170                     }
38171                     if(Roo.dd.DDM.locationCache[dd]){
38172                         delete Roo.dd.DDM.locationCache[dd];
38173                     }
38174                 }
38175                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38176             }
38177         }
38178         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38179         this.bind(null, null);
38180         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38181     },
38182
38183     handleLockChange : function(){
38184         this.refresh(true);
38185     },
38186
38187     onDenyColumnLock : function(){
38188
38189     },
38190
38191     onDenyColumnHide : function(){
38192
38193     },
38194
38195     handleHdMenuClick : function(item){
38196         var index = this.hdCtxIndex;
38197         var cm = this.cm, ds = this.ds;
38198         switch(item.id){
38199             case "asc":
38200                 ds.sort(cm.getDataIndex(index), "ASC");
38201                 break;
38202             case "desc":
38203                 ds.sort(cm.getDataIndex(index), "DESC");
38204                 break;
38205             case "lock":
38206                 var lc = cm.getLockedCount();
38207                 if(cm.getColumnCount(true) <= lc+1){
38208                     this.onDenyColumnLock();
38209                     return;
38210                 }
38211                 if(lc != index){
38212                     cm.setLocked(index, true, true);
38213                     cm.moveColumn(index, lc);
38214                     this.grid.fireEvent("columnmove", index, lc);
38215                 }else{
38216                     cm.setLocked(index, true);
38217                 }
38218             break;
38219             case "unlock":
38220                 var lc = cm.getLockedCount();
38221                 if((lc-1) != index){
38222                     cm.setLocked(index, false, true);
38223                     cm.moveColumn(index, lc-1);
38224                     this.grid.fireEvent("columnmove", index, lc-1);
38225                 }else{
38226                     cm.setLocked(index, false);
38227                 }
38228             break;
38229             case 'wider': // used to expand cols on touch..
38230             case 'narrow':
38231                 var cw = cm.getColumnWidth(index);
38232                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38233                 cw = Math.max(0, cw);
38234                 cw = Math.min(cw,4000);
38235                 cm.setColumnWidth(index, cw);
38236                 break;
38237                 
38238             default:
38239                 index = cm.getIndexById(item.id.substr(4));
38240                 if(index != -1){
38241                     if(item.checked && cm.getColumnCount(true) <= 1){
38242                         this.onDenyColumnHide();
38243                         return false;
38244                     }
38245                     cm.setHidden(index, item.checked);
38246                 }
38247         }
38248         return true;
38249     },
38250
38251     beforeColMenuShow : function(){
38252         var cm = this.cm,  colCount = cm.getColumnCount();
38253         this.colMenu.removeAll();
38254         for(var i = 0; i < colCount; i++){
38255             this.colMenu.add(new Roo.menu.CheckItem({
38256                 id: "col-"+cm.getColumnId(i),
38257                 text: cm.getColumnHeader(i),
38258                 checked: !cm.isHidden(i),
38259                 hideOnClick:false
38260             }));
38261         }
38262     },
38263
38264     handleHdCtx : function(g, index, e){
38265         e.stopEvent();
38266         var hd = this.getHeaderCell(index);
38267         this.hdCtxIndex = index;
38268         var ms = this.hmenu.items, cm = this.cm;
38269         ms.get("asc").setDisabled(!cm.isSortable(index));
38270         ms.get("desc").setDisabled(!cm.isSortable(index));
38271         if(this.grid.enableColLock !== false){
38272             ms.get("lock").setDisabled(cm.isLocked(index));
38273             ms.get("unlock").setDisabled(!cm.isLocked(index));
38274         }
38275         this.hmenu.show(hd, "tl-bl");
38276     },
38277
38278     handleHdOver : function(e){
38279         var hd = this.findHeaderCell(e.getTarget());
38280         if(hd && !this.headersDisabled){
38281             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38282                this.fly(hd).addClass("x-grid-hd-over");
38283             }
38284         }
38285     },
38286
38287     handleHdOut : function(e){
38288         var hd = this.findHeaderCell(e.getTarget());
38289         if(hd){
38290             this.fly(hd).removeClass("x-grid-hd-over");
38291         }
38292     },
38293
38294     handleSplitDblClick : function(e, t){
38295         var i = this.getCellIndex(t);
38296         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38297             this.autoSizeColumn(i, true);
38298             this.layout();
38299         }
38300     },
38301
38302     render : function(){
38303
38304         var cm = this.cm;
38305         var colCount = cm.getColumnCount();
38306
38307         if(this.grid.monitorWindowResize === true){
38308             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38309         }
38310         var header = this.renderHeaders();
38311         var body = this.templates.body.apply({rows:""});
38312         var html = this.templates.master.apply({
38313             lockedBody: body,
38314             body: body,
38315             lockedHeader: header[0],
38316             header: header[1]
38317         });
38318
38319         //this.updateColumns();
38320
38321         this.grid.getGridEl().dom.innerHTML = html;
38322
38323         this.initElements();
38324         
38325         // a kludge to fix the random scolling effect in webkit
38326         this.el.on("scroll", function() {
38327             this.el.dom.scrollTop=0; // hopefully not recursive..
38328         },this);
38329
38330         this.scroller.on("scroll", this.handleScroll, this);
38331         this.lockedBody.on("mousewheel", this.handleWheel, this);
38332         this.mainBody.on("mousewheel", this.handleWheel, this);
38333
38334         this.mainHd.on("mouseover", this.handleHdOver, this);
38335         this.mainHd.on("mouseout", this.handleHdOut, this);
38336         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38337                 {delegate: "."+this.splitClass});
38338
38339         this.lockedHd.on("mouseover", this.handleHdOver, this);
38340         this.lockedHd.on("mouseout", this.handleHdOut, this);
38341         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38342                 {delegate: "."+this.splitClass});
38343
38344         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38345             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38346         }
38347
38348         this.updateSplitters();
38349
38350         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38351             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38352             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38353         }
38354
38355         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38356             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38357             this.hmenu.add(
38358                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38359                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38360             );
38361             if(this.grid.enableColLock !== false){
38362                 this.hmenu.add('-',
38363                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38364                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38365                 );
38366             }
38367             if (Roo.isTouch) {
38368                  this.hmenu.add('-',
38369                     {id:"wider", text: this.columnsWiderText},
38370                     {id:"narrow", text: this.columnsNarrowText }
38371                 );
38372                 
38373                  
38374             }
38375             
38376             if(this.grid.enableColumnHide !== false){
38377
38378                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38379                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38380                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38381
38382                 this.hmenu.add('-',
38383                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38384                 );
38385             }
38386             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38387
38388             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38389         }
38390
38391         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38392             this.dd = new Roo.grid.GridDragZone(this.grid, {
38393                 ddGroup : this.grid.ddGroup || 'GridDD'
38394             });
38395             
38396         }
38397
38398         /*
38399         for(var i = 0; i < colCount; i++){
38400             if(cm.isHidden(i)){
38401                 this.hideColumn(i);
38402             }
38403             if(cm.config[i].align){
38404                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38405                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38406             }
38407         }*/
38408         
38409         this.updateHeaderSortState();
38410
38411         this.beforeInitialResize();
38412         this.layout(true);
38413
38414         // two part rendering gives faster view to the user
38415         this.renderPhase2.defer(1, this);
38416     },
38417
38418     renderPhase2 : function(){
38419         // render the rows now
38420         this.refresh();
38421         if(this.grid.autoSizeColumns){
38422             this.autoSizeColumns();
38423         }
38424     },
38425
38426     beforeInitialResize : function(){
38427
38428     },
38429
38430     onColumnSplitterMoved : function(i, w){
38431         this.userResized = true;
38432         var cm = this.grid.colModel;
38433         cm.setColumnWidth(i, w, true);
38434         var cid = cm.getColumnId(i);
38435         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38436         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38437         this.updateSplitters();
38438         this.layout();
38439         this.grid.fireEvent("columnresize", i, w);
38440     },
38441
38442     syncRowHeights : function(startIndex, endIndex){
38443         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38444             startIndex = startIndex || 0;
38445             var mrows = this.getBodyTable().rows;
38446             var lrows = this.getLockedTable().rows;
38447             var len = mrows.length-1;
38448             endIndex = Math.min(endIndex || len, len);
38449             for(var i = startIndex; i <= endIndex; i++){
38450                 var m = mrows[i], l = lrows[i];
38451                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38452                 m.style.height = l.style.height = h + "px";
38453             }
38454         }
38455     },
38456
38457     layout : function(initialRender, is2ndPass){
38458         var g = this.grid;
38459         var auto = g.autoHeight;
38460         var scrollOffset = 16;
38461         var c = g.getGridEl(), cm = this.cm,
38462                 expandCol = g.autoExpandColumn,
38463                 gv = this;
38464         //c.beginMeasure();
38465
38466         if(!c.dom.offsetWidth){ // display:none?
38467             if(initialRender){
38468                 this.lockedWrap.show();
38469                 this.mainWrap.show();
38470             }
38471             return;
38472         }
38473
38474         var hasLock = this.cm.isLocked(0);
38475
38476         var tbh = this.headerPanel.getHeight();
38477         var bbh = this.footerPanel.getHeight();
38478
38479         if(auto){
38480             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38481             var newHeight = ch + c.getBorderWidth("tb");
38482             if(g.maxHeight){
38483                 newHeight = Math.min(g.maxHeight, newHeight);
38484             }
38485             c.setHeight(newHeight);
38486         }
38487
38488         if(g.autoWidth){
38489             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38490         }
38491
38492         var s = this.scroller;
38493
38494         var csize = c.getSize(true);
38495
38496         this.el.setSize(csize.width, csize.height);
38497
38498         this.headerPanel.setWidth(csize.width);
38499         this.footerPanel.setWidth(csize.width);
38500
38501         var hdHeight = this.mainHd.getHeight();
38502         var vw = csize.width;
38503         var vh = csize.height - (tbh + bbh);
38504
38505         s.setSize(vw, vh);
38506
38507         var bt = this.getBodyTable();
38508         var ltWidth = hasLock ?
38509                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38510
38511         var scrollHeight = bt.offsetHeight;
38512         var scrollWidth = ltWidth + bt.offsetWidth;
38513         var vscroll = false, hscroll = false;
38514
38515         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38516
38517         var lw = this.lockedWrap, mw = this.mainWrap;
38518         var lb = this.lockedBody, mb = this.mainBody;
38519
38520         setTimeout(function(){
38521             var t = s.dom.offsetTop;
38522             var w = s.dom.clientWidth,
38523                 h = s.dom.clientHeight;
38524
38525             lw.setTop(t);
38526             lw.setSize(ltWidth, h);
38527
38528             mw.setLeftTop(ltWidth, t);
38529             mw.setSize(w-ltWidth, h);
38530
38531             lb.setHeight(h-hdHeight);
38532             mb.setHeight(h-hdHeight);
38533
38534             if(is2ndPass !== true && !gv.userResized && expandCol){
38535                 // high speed resize without full column calculation
38536                 
38537                 var ci = cm.getIndexById(expandCol);
38538                 if (ci < 0) {
38539                     ci = cm.findColumnIndex(expandCol);
38540                 }
38541                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38542                 var expandId = cm.getColumnId(ci);
38543                 var  tw = cm.getTotalWidth(false);
38544                 var currentWidth = cm.getColumnWidth(ci);
38545                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38546                 if(currentWidth != cw){
38547                     cm.setColumnWidth(ci, cw, true);
38548                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38549                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38550                     gv.updateSplitters();
38551                     gv.layout(false, true);
38552                 }
38553             }
38554
38555             if(initialRender){
38556                 lw.show();
38557                 mw.show();
38558             }
38559             //c.endMeasure();
38560         }, 10);
38561     },
38562
38563     onWindowResize : function(){
38564         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38565             return;
38566         }
38567         this.layout();
38568     },
38569
38570     appendFooter : function(parentEl){
38571         return null;
38572     },
38573
38574     sortAscText : "Sort Ascending",
38575     sortDescText : "Sort Descending",
38576     lockText : "Lock Column",
38577     unlockText : "Unlock Column",
38578     columnsText : "Columns",
38579  
38580     columnsWiderText : "Wider",
38581     columnsNarrowText : "Thinner"
38582 });
38583
38584
38585 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38586     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38587     this.proxy.el.addClass('x-grid3-col-dd');
38588 };
38589
38590 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38591     handleMouseDown : function(e){
38592
38593     },
38594
38595     callHandleMouseDown : function(e){
38596         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38597     }
38598 });
38599 /*
38600  * Based on:
38601  * Ext JS Library 1.1.1
38602  * Copyright(c) 2006-2007, Ext JS, LLC.
38603  *
38604  * Originally Released Under LGPL - original licence link has changed is not relivant.
38605  *
38606  * Fork - LGPL
38607  * <script type="text/javascript">
38608  */
38609  
38610 // private
38611 // This is a support class used internally by the Grid components
38612 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38613     this.grid = grid;
38614     this.view = grid.getView();
38615     this.proxy = this.view.resizeProxy;
38616     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38617         "gridSplitters" + this.grid.getGridEl().id, {
38618         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38619     });
38620     this.setHandleElId(Roo.id(hd));
38621     this.setOuterHandleElId(Roo.id(hd2));
38622     this.scroll = false;
38623 };
38624 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38625     fly: Roo.Element.fly,
38626
38627     b4StartDrag : function(x, y){
38628         this.view.headersDisabled = true;
38629         this.proxy.setHeight(this.view.mainWrap.getHeight());
38630         var w = this.cm.getColumnWidth(this.cellIndex);
38631         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38632         this.resetConstraints();
38633         this.setXConstraint(minw, 1000);
38634         this.setYConstraint(0, 0);
38635         this.minX = x - minw;
38636         this.maxX = x + 1000;
38637         this.startPos = x;
38638         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38639     },
38640
38641
38642     handleMouseDown : function(e){
38643         ev = Roo.EventObject.setEvent(e);
38644         var t = this.fly(ev.getTarget());
38645         if(t.hasClass("x-grid-split")){
38646             this.cellIndex = this.view.getCellIndex(t.dom);
38647             this.split = t.dom;
38648             this.cm = this.grid.colModel;
38649             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38650                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38651             }
38652         }
38653     },
38654
38655     endDrag : function(e){
38656         this.view.headersDisabled = false;
38657         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38658         var diff = endX - this.startPos;
38659         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38660     },
38661
38662     autoOffset : function(){
38663         this.setDelta(0,0);
38664     }
38665 });/*
38666  * Based on:
38667  * Ext JS Library 1.1.1
38668  * Copyright(c) 2006-2007, Ext JS, LLC.
38669  *
38670  * Originally Released Under LGPL - original licence link has changed is not relivant.
38671  *
38672  * Fork - LGPL
38673  * <script type="text/javascript">
38674  */
38675  
38676 // private
38677 // This is a support class used internally by the Grid components
38678 Roo.grid.GridDragZone = function(grid, config){
38679     this.view = grid.getView();
38680     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38681     if(this.view.lockedBody){
38682         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38683         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38684     }
38685     this.scroll = false;
38686     this.grid = grid;
38687     this.ddel = document.createElement('div');
38688     this.ddel.className = 'x-grid-dd-wrap';
38689 };
38690
38691 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38692     ddGroup : "GridDD",
38693
38694     getDragData : function(e){
38695         var t = Roo.lib.Event.getTarget(e);
38696         var rowIndex = this.view.findRowIndex(t);
38697         var sm = this.grid.selModel;
38698             
38699         //Roo.log(rowIndex);
38700         
38701         if (sm.getSelectedCell) {
38702             // cell selection..
38703             if (!sm.getSelectedCell()) {
38704                 return false;
38705             }
38706             if (rowIndex != sm.getSelectedCell()[0]) {
38707                 return false;
38708             }
38709         
38710         }
38711         
38712         if(rowIndex !== false){
38713             
38714             // if editorgrid.. 
38715             
38716             
38717             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38718                
38719             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38720               //  
38721             //}
38722             if (e.hasModifier()){
38723                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38724             }
38725             
38726             Roo.log("getDragData");
38727             
38728             return {
38729                 grid: this.grid,
38730                 ddel: this.ddel,
38731                 rowIndex: rowIndex,
38732                 selections:sm.getSelections ? sm.getSelections() : (
38733                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38734                 )
38735             };
38736         }
38737         return false;
38738     },
38739
38740     onInitDrag : function(e){
38741         var data = this.dragData;
38742         this.ddel.innerHTML = this.grid.getDragDropText();
38743         this.proxy.update(this.ddel);
38744         // fire start drag?
38745     },
38746
38747     afterRepair : function(){
38748         this.dragging = false;
38749     },
38750
38751     getRepairXY : function(e, data){
38752         return false;
38753     },
38754
38755     onEndDrag : function(data, e){
38756         // fire end drag?
38757     },
38758
38759     onValidDrop : function(dd, e, id){
38760         // fire drag drop?
38761         this.hideProxy();
38762     },
38763
38764     beforeInvalidDrop : function(e, id){
38765
38766     }
38767 });/*
38768  * Based on:
38769  * Ext JS Library 1.1.1
38770  * Copyright(c) 2006-2007, Ext JS, LLC.
38771  *
38772  * Originally Released Under LGPL - original licence link has changed is not relivant.
38773  *
38774  * Fork - LGPL
38775  * <script type="text/javascript">
38776  */
38777  
38778
38779 /**
38780  * @class Roo.grid.ColumnModel
38781  * @extends Roo.util.Observable
38782  * This is the default implementation of a ColumnModel used by the Grid. It defines
38783  * the columns in the grid.
38784  * <br>Usage:<br>
38785  <pre><code>
38786  var colModel = new Roo.grid.ColumnModel([
38787         {header: "Ticker", width: 60, sortable: true, locked: true},
38788         {header: "Company Name", width: 150, sortable: true},
38789         {header: "Market Cap.", width: 100, sortable: true},
38790         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38791         {header: "Employees", width: 100, sortable: true, resizable: false}
38792  ]);
38793  </code></pre>
38794  * <p>
38795  
38796  * The config options listed for this class are options which may appear in each
38797  * individual column definition.
38798  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38799  * @constructor
38800  * @param {Object} config An Array of column config objects. See this class's
38801  * config objects for details.
38802 */
38803 Roo.grid.ColumnModel = function(config){
38804         /**
38805      * The config passed into the constructor
38806      */
38807     this.config = config;
38808     this.lookup = {};
38809
38810     // if no id, create one
38811     // if the column does not have a dataIndex mapping,
38812     // map it to the order it is in the config
38813     for(var i = 0, len = config.length; i < len; i++){
38814         var c = config[i];
38815         if(typeof c.dataIndex == "undefined"){
38816             c.dataIndex = i;
38817         }
38818         if(typeof c.renderer == "string"){
38819             c.renderer = Roo.util.Format[c.renderer];
38820         }
38821         if(typeof c.id == "undefined"){
38822             c.id = Roo.id();
38823         }
38824         if(c.editor && c.editor.xtype){
38825             c.editor  = Roo.factory(c.editor, Roo.grid);
38826         }
38827         if(c.editor && c.editor.isFormField){
38828             c.editor = new Roo.grid.GridEditor(c.editor);
38829         }
38830         this.lookup[c.id] = c;
38831     }
38832
38833     /**
38834      * The width of columns which have no width specified (defaults to 100)
38835      * @type Number
38836      */
38837     this.defaultWidth = 100;
38838
38839     /**
38840      * Default sortable of columns which have no sortable specified (defaults to false)
38841      * @type Boolean
38842      */
38843     this.defaultSortable = false;
38844
38845     this.addEvents({
38846         /**
38847              * @event widthchange
38848              * Fires when the width of a column changes.
38849              * @param {ColumnModel} this
38850              * @param {Number} columnIndex The column index
38851              * @param {Number} newWidth The new width
38852              */
38853             "widthchange": true,
38854         /**
38855              * @event headerchange
38856              * Fires when the text of a header changes.
38857              * @param {ColumnModel} this
38858              * @param {Number} columnIndex The column index
38859              * @param {Number} newText The new header text
38860              */
38861             "headerchange": true,
38862         /**
38863              * @event hiddenchange
38864              * Fires when a column is hidden or "unhidden".
38865              * @param {ColumnModel} this
38866              * @param {Number} columnIndex The column index
38867              * @param {Boolean} hidden true if hidden, false otherwise
38868              */
38869             "hiddenchange": true,
38870             /**
38871          * @event columnmoved
38872          * Fires when a column is moved.
38873          * @param {ColumnModel} this
38874          * @param {Number} oldIndex
38875          * @param {Number} newIndex
38876          */
38877         "columnmoved" : true,
38878         /**
38879          * @event columlockchange
38880          * Fires when a column's locked state is changed
38881          * @param {ColumnModel} this
38882          * @param {Number} colIndex
38883          * @param {Boolean} locked true if locked
38884          */
38885         "columnlockchange" : true
38886     });
38887     Roo.grid.ColumnModel.superclass.constructor.call(this);
38888 };
38889 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38890     /**
38891      * @cfg {String} header The header text to display in the Grid view.
38892      */
38893     /**
38894      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38895      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38896      * specified, the column's index is used as an index into the Record's data Array.
38897      */
38898     /**
38899      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38900      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38901      */
38902     /**
38903      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38904      * Defaults to the value of the {@link #defaultSortable} property.
38905      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38906      */
38907     /**
38908      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38909      */
38910     /**
38911      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38912      */
38913     /**
38914      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38915      */
38916     /**
38917      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38918      */
38919     /**
38920      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38921      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38922      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38923      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38924      */
38925        /**
38926      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38927      */
38928     /**
38929      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38930      */
38931     /**
38932      * @cfg {String} cursor (Optional)
38933      */
38934     /**
38935      * @cfg {String} tooltip (Optional)
38936      */
38937     /**
38938      * Returns the id of the column at the specified index.
38939      * @param {Number} index The column index
38940      * @return {String} the id
38941      */
38942     getColumnId : function(index){
38943         return this.config[index].id;
38944     },
38945
38946     /**
38947      * Returns the column for a specified id.
38948      * @param {String} id The column id
38949      * @return {Object} the column
38950      */
38951     getColumnById : function(id){
38952         return this.lookup[id];
38953     },
38954
38955     
38956     /**
38957      * Returns the column for a specified dataIndex.
38958      * @param {String} dataIndex The column dataIndex
38959      * @return {Object|Boolean} the column or false if not found
38960      */
38961     getColumnByDataIndex: function(dataIndex){
38962         var index = this.findColumnIndex(dataIndex);
38963         return index > -1 ? this.config[index] : false;
38964     },
38965     
38966     /**
38967      * Returns the index for a specified column id.
38968      * @param {String} id The column id
38969      * @return {Number} the index, or -1 if not found
38970      */
38971     getIndexById : function(id){
38972         for(var i = 0, len = this.config.length; i < len; i++){
38973             if(this.config[i].id == id){
38974                 return i;
38975             }
38976         }
38977         return -1;
38978     },
38979     
38980     /**
38981      * Returns the index for a specified column dataIndex.
38982      * @param {String} dataIndex The column dataIndex
38983      * @return {Number} the index, or -1 if not found
38984      */
38985     
38986     findColumnIndex : function(dataIndex){
38987         for(var i = 0, len = this.config.length; i < len; i++){
38988             if(this.config[i].dataIndex == dataIndex){
38989                 return i;
38990             }
38991         }
38992         return -1;
38993     },
38994     
38995     
38996     moveColumn : function(oldIndex, newIndex){
38997         var c = this.config[oldIndex];
38998         this.config.splice(oldIndex, 1);
38999         this.config.splice(newIndex, 0, c);
39000         this.dataMap = null;
39001         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39002     },
39003
39004     isLocked : function(colIndex){
39005         return this.config[colIndex].locked === true;
39006     },
39007
39008     setLocked : function(colIndex, value, suppressEvent){
39009         if(this.isLocked(colIndex) == value){
39010             return;
39011         }
39012         this.config[colIndex].locked = value;
39013         if(!suppressEvent){
39014             this.fireEvent("columnlockchange", this, colIndex, value);
39015         }
39016     },
39017
39018     getTotalLockedWidth : function(){
39019         var totalWidth = 0;
39020         for(var i = 0; i < this.config.length; i++){
39021             if(this.isLocked(i) && !this.isHidden(i)){
39022                 this.totalWidth += this.getColumnWidth(i);
39023             }
39024         }
39025         return totalWidth;
39026     },
39027
39028     getLockedCount : function(){
39029         for(var i = 0, len = this.config.length; i < len; i++){
39030             if(!this.isLocked(i)){
39031                 return i;
39032             }
39033         }
39034     },
39035
39036     /**
39037      * Returns the number of columns.
39038      * @return {Number}
39039      */
39040     getColumnCount : function(visibleOnly){
39041         if(visibleOnly === true){
39042             var c = 0;
39043             for(var i = 0, len = this.config.length; i < len; i++){
39044                 if(!this.isHidden(i)){
39045                     c++;
39046                 }
39047             }
39048             return c;
39049         }
39050         return this.config.length;
39051     },
39052
39053     /**
39054      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39055      * @param {Function} fn
39056      * @param {Object} scope (optional)
39057      * @return {Array} result
39058      */
39059     getColumnsBy : function(fn, scope){
39060         var r = [];
39061         for(var i = 0, len = this.config.length; i < len; i++){
39062             var c = this.config[i];
39063             if(fn.call(scope||this, c, i) === true){
39064                 r[r.length] = c;
39065             }
39066         }
39067         return r;
39068     },
39069
39070     /**
39071      * Returns true if the specified column is sortable.
39072      * @param {Number} col The column index
39073      * @return {Boolean}
39074      */
39075     isSortable : function(col){
39076         if(typeof this.config[col].sortable == "undefined"){
39077             return this.defaultSortable;
39078         }
39079         return this.config[col].sortable;
39080     },
39081
39082     /**
39083      * Returns the rendering (formatting) function defined for the column.
39084      * @param {Number} col The column index.
39085      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39086      */
39087     getRenderer : function(col){
39088         if(!this.config[col].renderer){
39089             return Roo.grid.ColumnModel.defaultRenderer;
39090         }
39091         return this.config[col].renderer;
39092     },
39093
39094     /**
39095      * Sets the rendering (formatting) function for a column.
39096      * @param {Number} col The column index
39097      * @param {Function} fn The function to use to process the cell's raw data
39098      * to return HTML markup for the grid view. The render function is called with
39099      * the following parameters:<ul>
39100      * <li>Data value.</li>
39101      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39102      * <li>css A CSS style string to apply to the table cell.</li>
39103      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39104      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39105      * <li>Row index</li>
39106      * <li>Column index</li>
39107      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39108      */
39109     setRenderer : function(col, fn){
39110         this.config[col].renderer = fn;
39111     },
39112
39113     /**
39114      * Returns the width for the specified column.
39115      * @param {Number} col The column index
39116      * @return {Number}
39117      */
39118     getColumnWidth : function(col){
39119         return this.config[col].width * 1 || this.defaultWidth;
39120     },
39121
39122     /**
39123      * Sets the width for a column.
39124      * @param {Number} col The column index
39125      * @param {Number} width The new width
39126      */
39127     setColumnWidth : function(col, width, suppressEvent){
39128         this.config[col].width = width;
39129         this.totalWidth = null;
39130         if(!suppressEvent){
39131              this.fireEvent("widthchange", this, col, width);
39132         }
39133     },
39134
39135     /**
39136      * Returns the total width of all columns.
39137      * @param {Boolean} includeHidden True to include hidden column widths
39138      * @return {Number}
39139      */
39140     getTotalWidth : function(includeHidden){
39141         if(!this.totalWidth){
39142             this.totalWidth = 0;
39143             for(var i = 0, len = this.config.length; i < len; i++){
39144                 if(includeHidden || !this.isHidden(i)){
39145                     this.totalWidth += this.getColumnWidth(i);
39146                 }
39147             }
39148         }
39149         return this.totalWidth;
39150     },
39151
39152     /**
39153      * Returns the header for the specified column.
39154      * @param {Number} col The column index
39155      * @return {String}
39156      */
39157     getColumnHeader : function(col){
39158         return this.config[col].header;
39159     },
39160
39161     /**
39162      * Sets the header for a column.
39163      * @param {Number} col The column index
39164      * @param {String} header The new header
39165      */
39166     setColumnHeader : function(col, header){
39167         this.config[col].header = header;
39168         this.fireEvent("headerchange", this, col, header);
39169     },
39170
39171     /**
39172      * Returns the tooltip for the specified column.
39173      * @param {Number} col The column index
39174      * @return {String}
39175      */
39176     getColumnTooltip : function(col){
39177             return this.config[col].tooltip;
39178     },
39179     /**
39180      * Sets the tooltip for a column.
39181      * @param {Number} col The column index
39182      * @param {String} tooltip The new tooltip
39183      */
39184     setColumnTooltip : function(col, tooltip){
39185             this.config[col].tooltip = tooltip;
39186     },
39187
39188     /**
39189      * Returns the dataIndex for the specified column.
39190      * @param {Number} col The column index
39191      * @return {Number}
39192      */
39193     getDataIndex : function(col){
39194         return this.config[col].dataIndex;
39195     },
39196
39197     /**
39198      * Sets the dataIndex for a column.
39199      * @param {Number} col The column index
39200      * @param {Number} dataIndex The new dataIndex
39201      */
39202     setDataIndex : function(col, dataIndex){
39203         this.config[col].dataIndex = dataIndex;
39204     },
39205
39206     
39207     
39208     /**
39209      * Returns true if the cell is editable.
39210      * @param {Number} colIndex The column index
39211      * @param {Number} rowIndex The row index
39212      * @return {Boolean}
39213      */
39214     isCellEditable : function(colIndex, rowIndex){
39215         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39216     },
39217
39218     /**
39219      * Returns the editor defined for the cell/column.
39220      * return false or null to disable editing.
39221      * @param {Number} colIndex The column index
39222      * @param {Number} rowIndex The row index
39223      * @return {Object}
39224      */
39225     getCellEditor : function(colIndex, rowIndex){
39226         return this.config[colIndex].editor;
39227     },
39228
39229     /**
39230      * Sets if a column is editable.
39231      * @param {Number} col The column index
39232      * @param {Boolean} editable True if the column is editable
39233      */
39234     setEditable : function(col, editable){
39235         this.config[col].editable = editable;
39236     },
39237
39238
39239     /**
39240      * Returns true if the column is hidden.
39241      * @param {Number} colIndex The column index
39242      * @return {Boolean}
39243      */
39244     isHidden : function(colIndex){
39245         return this.config[colIndex].hidden;
39246     },
39247
39248
39249     /**
39250      * Returns true if the column width cannot be changed
39251      */
39252     isFixed : function(colIndex){
39253         return this.config[colIndex].fixed;
39254     },
39255
39256     /**
39257      * Returns true if the column can be resized
39258      * @return {Boolean}
39259      */
39260     isResizable : function(colIndex){
39261         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39262     },
39263     /**
39264      * Sets if a column is hidden.
39265      * @param {Number} colIndex The column index
39266      * @param {Boolean} hidden True if the column is hidden
39267      */
39268     setHidden : function(colIndex, hidden){
39269         this.config[colIndex].hidden = hidden;
39270         this.totalWidth = null;
39271         this.fireEvent("hiddenchange", this, colIndex, hidden);
39272     },
39273
39274     /**
39275      * Sets the editor for a column.
39276      * @param {Number} col The column index
39277      * @param {Object} editor The editor object
39278      */
39279     setEditor : function(col, editor){
39280         this.config[col].editor = editor;
39281     }
39282 });
39283
39284 Roo.grid.ColumnModel.defaultRenderer = function(value){
39285         if(typeof value == "string" && value.length < 1){
39286             return "&#160;";
39287         }
39288         return value;
39289 };
39290
39291 // Alias for backwards compatibility
39292 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39293 /*
39294  * Based on:
39295  * Ext JS Library 1.1.1
39296  * Copyright(c) 2006-2007, Ext JS, LLC.
39297  *
39298  * Originally Released Under LGPL - original licence link has changed is not relivant.
39299  *
39300  * Fork - LGPL
39301  * <script type="text/javascript">
39302  */
39303
39304 /**
39305  * @class Roo.grid.AbstractSelectionModel
39306  * @extends Roo.util.Observable
39307  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39308  * implemented by descendant classes.  This class should not be directly instantiated.
39309  * @constructor
39310  */
39311 Roo.grid.AbstractSelectionModel = function(){
39312     this.locked = false;
39313     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39314 };
39315
39316 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39317     /** @ignore Called by the grid automatically. Do not call directly. */
39318     init : function(grid){
39319         this.grid = grid;
39320         this.initEvents();
39321     },
39322
39323     /**
39324      * Locks the selections.
39325      */
39326     lock : function(){
39327         this.locked = true;
39328     },
39329
39330     /**
39331      * Unlocks the selections.
39332      */
39333     unlock : function(){
39334         this.locked = false;
39335     },
39336
39337     /**
39338      * Returns true if the selections are locked.
39339      * @return {Boolean}
39340      */
39341     isLocked : function(){
39342         return this.locked;
39343     }
39344 });/*
39345  * Based on:
39346  * Ext JS Library 1.1.1
39347  * Copyright(c) 2006-2007, Ext JS, LLC.
39348  *
39349  * Originally Released Under LGPL - original licence link has changed is not relivant.
39350  *
39351  * Fork - LGPL
39352  * <script type="text/javascript">
39353  */
39354 /**
39355  * @extends Roo.grid.AbstractSelectionModel
39356  * @class Roo.grid.RowSelectionModel
39357  * The default SelectionModel used by {@link Roo.grid.Grid}.
39358  * It supports multiple selections and keyboard selection/navigation. 
39359  * @constructor
39360  * @param {Object} config
39361  */
39362 Roo.grid.RowSelectionModel = function(config){
39363     Roo.apply(this, config);
39364     this.selections = new Roo.util.MixedCollection(false, function(o){
39365         return o.id;
39366     });
39367
39368     this.last = false;
39369     this.lastActive = false;
39370
39371     this.addEvents({
39372         /**
39373              * @event selectionchange
39374              * Fires when the selection changes
39375              * @param {SelectionModel} this
39376              */
39377             "selectionchange" : true,
39378         /**
39379              * @event afterselectionchange
39380              * Fires after the selection changes (eg. by key press or clicking)
39381              * @param {SelectionModel} this
39382              */
39383             "afterselectionchange" : true,
39384         /**
39385              * @event beforerowselect
39386              * Fires when a row is selected being selected, return false to cancel.
39387              * @param {SelectionModel} this
39388              * @param {Number} rowIndex The selected index
39389              * @param {Boolean} keepExisting False if other selections will be cleared
39390              */
39391             "beforerowselect" : true,
39392         /**
39393              * @event rowselect
39394              * Fires when a row is selected.
39395              * @param {SelectionModel} this
39396              * @param {Number} rowIndex The selected index
39397              * @param {Roo.data.Record} r The record
39398              */
39399             "rowselect" : true,
39400         /**
39401              * @event rowdeselect
39402              * Fires when a row is deselected.
39403              * @param {SelectionModel} this
39404              * @param {Number} rowIndex The selected index
39405              */
39406         "rowdeselect" : true
39407     });
39408     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39409     this.locked = false;
39410 };
39411
39412 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39413     /**
39414      * @cfg {Boolean} singleSelect
39415      * True to allow selection of only one row at a time (defaults to false)
39416      */
39417     singleSelect : false,
39418
39419     // private
39420     initEvents : function(){
39421
39422         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39423             this.grid.on("mousedown", this.handleMouseDown, this);
39424         }else{ // allow click to work like normal
39425             this.grid.on("rowclick", this.handleDragableRowClick, this);
39426         }
39427
39428         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39429             "up" : function(e){
39430                 if(!e.shiftKey){
39431                     this.selectPrevious(e.shiftKey);
39432                 }else if(this.last !== false && this.lastActive !== false){
39433                     var last = this.last;
39434                     this.selectRange(this.last,  this.lastActive-1);
39435                     this.grid.getView().focusRow(this.lastActive);
39436                     if(last !== false){
39437                         this.last = last;
39438                     }
39439                 }else{
39440                     this.selectFirstRow();
39441                 }
39442                 this.fireEvent("afterselectionchange", this);
39443             },
39444             "down" : function(e){
39445                 if(!e.shiftKey){
39446                     this.selectNext(e.shiftKey);
39447                 }else if(this.last !== false && this.lastActive !== false){
39448                     var last = this.last;
39449                     this.selectRange(this.last,  this.lastActive+1);
39450                     this.grid.getView().focusRow(this.lastActive);
39451                     if(last !== false){
39452                         this.last = last;
39453                     }
39454                 }else{
39455                     this.selectFirstRow();
39456                 }
39457                 this.fireEvent("afterselectionchange", this);
39458             },
39459             scope: this
39460         });
39461
39462         var view = this.grid.view;
39463         view.on("refresh", this.onRefresh, this);
39464         view.on("rowupdated", this.onRowUpdated, this);
39465         view.on("rowremoved", this.onRemove, this);
39466     },
39467
39468     // private
39469     onRefresh : function(){
39470         var ds = this.grid.dataSource, i, v = this.grid.view;
39471         var s = this.selections;
39472         s.each(function(r){
39473             if((i = ds.indexOfId(r.id)) != -1){
39474                 v.onRowSelect(i);
39475             }else{
39476                 s.remove(r);
39477             }
39478         });
39479     },
39480
39481     // private
39482     onRemove : function(v, index, r){
39483         this.selections.remove(r);
39484     },
39485
39486     // private
39487     onRowUpdated : function(v, index, r){
39488         if(this.isSelected(r)){
39489             v.onRowSelect(index);
39490         }
39491     },
39492
39493     /**
39494      * Select records.
39495      * @param {Array} records The records to select
39496      * @param {Boolean} keepExisting (optional) True to keep existing selections
39497      */
39498     selectRecords : function(records, keepExisting){
39499         if(!keepExisting){
39500             this.clearSelections();
39501         }
39502         var ds = this.grid.dataSource;
39503         for(var i = 0, len = records.length; i < len; i++){
39504             this.selectRow(ds.indexOf(records[i]), true);
39505         }
39506     },
39507
39508     /**
39509      * Gets the number of selected rows.
39510      * @return {Number}
39511      */
39512     getCount : function(){
39513         return this.selections.length;
39514     },
39515
39516     /**
39517      * Selects the first row in the grid.
39518      */
39519     selectFirstRow : function(){
39520         this.selectRow(0);
39521     },
39522
39523     /**
39524      * Select the last row.
39525      * @param {Boolean} keepExisting (optional) True to keep existing selections
39526      */
39527     selectLastRow : function(keepExisting){
39528         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39529     },
39530
39531     /**
39532      * Selects the row immediately following the last selected row.
39533      * @param {Boolean} keepExisting (optional) True to keep existing selections
39534      */
39535     selectNext : function(keepExisting){
39536         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39537             this.selectRow(this.last+1, keepExisting);
39538             this.grid.getView().focusRow(this.last);
39539         }
39540     },
39541
39542     /**
39543      * Selects the row that precedes the last selected row.
39544      * @param {Boolean} keepExisting (optional) True to keep existing selections
39545      */
39546     selectPrevious : function(keepExisting){
39547         if(this.last){
39548             this.selectRow(this.last-1, keepExisting);
39549             this.grid.getView().focusRow(this.last);
39550         }
39551     },
39552
39553     /**
39554      * Returns the selected records
39555      * @return {Array} Array of selected records
39556      */
39557     getSelections : function(){
39558         return [].concat(this.selections.items);
39559     },
39560
39561     /**
39562      * Returns the first selected record.
39563      * @return {Record}
39564      */
39565     getSelected : function(){
39566         return this.selections.itemAt(0);
39567     },
39568
39569
39570     /**
39571      * Clears all selections.
39572      */
39573     clearSelections : function(fast){
39574         if(this.locked) return;
39575         if(fast !== true){
39576             var ds = this.grid.dataSource;
39577             var s = this.selections;
39578             s.each(function(r){
39579                 this.deselectRow(ds.indexOfId(r.id));
39580             }, this);
39581             s.clear();
39582         }else{
39583             this.selections.clear();
39584         }
39585         this.last = false;
39586     },
39587
39588
39589     /**
39590      * Selects all rows.
39591      */
39592     selectAll : function(){
39593         if(this.locked) return;
39594         this.selections.clear();
39595         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39596             this.selectRow(i, true);
39597         }
39598     },
39599
39600     /**
39601      * Returns True if there is a selection.
39602      * @return {Boolean}
39603      */
39604     hasSelection : function(){
39605         return this.selections.length > 0;
39606     },
39607
39608     /**
39609      * Returns True if the specified row is selected.
39610      * @param {Number/Record} record The record or index of the record to check
39611      * @return {Boolean}
39612      */
39613     isSelected : function(index){
39614         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39615         return (r && this.selections.key(r.id) ? true : false);
39616     },
39617
39618     /**
39619      * Returns True if the specified record id is selected.
39620      * @param {String} id The id of record to check
39621      * @return {Boolean}
39622      */
39623     isIdSelected : function(id){
39624         return (this.selections.key(id) ? true : false);
39625     },
39626
39627     // private
39628     handleMouseDown : function(e, t){
39629         var view = this.grid.getView(), rowIndex;
39630         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39631             return;
39632         };
39633         if(e.shiftKey && this.last !== false){
39634             var last = this.last;
39635             this.selectRange(last, rowIndex, e.ctrlKey);
39636             this.last = last; // reset the last
39637             view.focusRow(rowIndex);
39638         }else{
39639             var isSelected = this.isSelected(rowIndex);
39640             if(e.button !== 0 && isSelected){
39641                 view.focusRow(rowIndex);
39642             }else if(e.ctrlKey && isSelected){
39643                 this.deselectRow(rowIndex);
39644             }else if(!isSelected){
39645                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39646                 view.focusRow(rowIndex);
39647             }
39648         }
39649         this.fireEvent("afterselectionchange", this);
39650     },
39651     // private
39652     handleDragableRowClick :  function(grid, rowIndex, e) 
39653     {
39654         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39655             this.selectRow(rowIndex, false);
39656             grid.view.focusRow(rowIndex);
39657              this.fireEvent("afterselectionchange", this);
39658         }
39659     },
39660     
39661     /**
39662      * Selects multiple rows.
39663      * @param {Array} rows Array of the indexes of the row to select
39664      * @param {Boolean} keepExisting (optional) True to keep existing selections
39665      */
39666     selectRows : function(rows, keepExisting){
39667         if(!keepExisting){
39668             this.clearSelections();
39669         }
39670         for(var i = 0, len = rows.length; i < len; i++){
39671             this.selectRow(rows[i], true);
39672         }
39673     },
39674
39675     /**
39676      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39677      * @param {Number} startRow The index of the first row in the range
39678      * @param {Number} endRow The index of the last row in the range
39679      * @param {Boolean} keepExisting (optional) True to retain existing selections
39680      */
39681     selectRange : function(startRow, endRow, keepExisting){
39682         if(this.locked) return;
39683         if(!keepExisting){
39684             this.clearSelections();
39685         }
39686         if(startRow <= endRow){
39687             for(var i = startRow; i <= endRow; i++){
39688                 this.selectRow(i, true);
39689             }
39690         }else{
39691             for(var i = startRow; i >= endRow; i--){
39692                 this.selectRow(i, true);
39693             }
39694         }
39695     },
39696
39697     /**
39698      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39699      * @param {Number} startRow The index of the first row in the range
39700      * @param {Number} endRow The index of the last row in the range
39701      */
39702     deselectRange : function(startRow, endRow, preventViewNotify){
39703         if(this.locked) return;
39704         for(var i = startRow; i <= endRow; i++){
39705             this.deselectRow(i, preventViewNotify);
39706         }
39707     },
39708
39709     /**
39710      * Selects a row.
39711      * @param {Number} row The index of the row to select
39712      * @param {Boolean} keepExisting (optional) True to keep existing selections
39713      */
39714     selectRow : function(index, keepExisting, preventViewNotify){
39715         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39716         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39717             if(!keepExisting || this.singleSelect){
39718                 this.clearSelections();
39719             }
39720             var r = this.grid.dataSource.getAt(index);
39721             this.selections.add(r);
39722             this.last = this.lastActive = index;
39723             if(!preventViewNotify){
39724                 this.grid.getView().onRowSelect(index);
39725             }
39726             this.fireEvent("rowselect", this, index, r);
39727             this.fireEvent("selectionchange", this);
39728         }
39729     },
39730
39731     /**
39732      * Deselects a row.
39733      * @param {Number} row The index of the row to deselect
39734      */
39735     deselectRow : function(index, preventViewNotify){
39736         if(this.locked) return;
39737         if(this.last == index){
39738             this.last = false;
39739         }
39740         if(this.lastActive == index){
39741             this.lastActive = false;
39742         }
39743         var r = this.grid.dataSource.getAt(index);
39744         this.selections.remove(r);
39745         if(!preventViewNotify){
39746             this.grid.getView().onRowDeselect(index);
39747         }
39748         this.fireEvent("rowdeselect", this, index);
39749         this.fireEvent("selectionchange", this);
39750     },
39751
39752     // private
39753     restoreLast : function(){
39754         if(this._last){
39755             this.last = this._last;
39756         }
39757     },
39758
39759     // private
39760     acceptsNav : function(row, col, cm){
39761         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39762     },
39763
39764     // private
39765     onEditorKey : function(field, e){
39766         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39767         if(k == e.TAB){
39768             e.stopEvent();
39769             ed.completeEdit();
39770             if(e.shiftKey){
39771                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39772             }else{
39773                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39774             }
39775         }else if(k == e.ENTER && !e.ctrlKey){
39776             e.stopEvent();
39777             ed.completeEdit();
39778             if(e.shiftKey){
39779                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39780             }else{
39781                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39782             }
39783         }else if(k == e.ESC){
39784             ed.cancelEdit();
39785         }
39786         if(newCell){
39787             g.startEditing(newCell[0], newCell[1]);
39788         }
39789     }
39790 });/*
39791  * Based on:
39792  * Ext JS Library 1.1.1
39793  * Copyright(c) 2006-2007, Ext JS, LLC.
39794  *
39795  * Originally Released Under LGPL - original licence link has changed is not relivant.
39796  *
39797  * Fork - LGPL
39798  * <script type="text/javascript">
39799  */
39800 /**
39801  * @class Roo.grid.CellSelectionModel
39802  * @extends Roo.grid.AbstractSelectionModel
39803  * This class provides the basic implementation for cell selection in a grid.
39804  * @constructor
39805  * @param {Object} config The object containing the configuration of this model.
39806  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39807  */
39808 Roo.grid.CellSelectionModel = function(config){
39809     Roo.apply(this, config);
39810
39811     this.selection = null;
39812
39813     this.addEvents({
39814         /**
39815              * @event beforerowselect
39816              * Fires before a cell is selected.
39817              * @param {SelectionModel} this
39818              * @param {Number} rowIndex The selected row index
39819              * @param {Number} colIndex The selected cell index
39820              */
39821             "beforecellselect" : true,
39822         /**
39823              * @event cellselect
39824              * Fires when a cell is selected.
39825              * @param {SelectionModel} this
39826              * @param {Number} rowIndex The selected row index
39827              * @param {Number} colIndex The selected cell index
39828              */
39829             "cellselect" : true,
39830         /**
39831              * @event selectionchange
39832              * Fires when the active selection changes.
39833              * @param {SelectionModel} this
39834              * @param {Object} selection null for no selection or an object (o) with two properties
39835                 <ul>
39836                 <li>o.record: the record object for the row the selection is in</li>
39837                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39838                 </ul>
39839              */
39840             "selectionchange" : true,
39841         /**
39842              * @event tabend
39843              * Fires when the tab (or enter) was pressed on the last editable cell
39844              * You can use this to trigger add new row.
39845              * @param {SelectionModel} this
39846              */
39847             "tabend" : true,
39848          /**
39849              * @event beforeeditnext
39850              * Fires before the next editable sell is made active
39851              * You can use this to skip to another cell or fire the tabend
39852              *    if you set cell to false
39853              * @param {Object} eventdata object : { cell : [ row, col ] } 
39854              */
39855             "beforeeditnext" : true
39856     });
39857     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39858 };
39859
39860 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39861     
39862     enter_is_tab: false,
39863
39864     /** @ignore */
39865     initEvents : function(){
39866         this.grid.on("mousedown", this.handleMouseDown, this);
39867         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39868         var view = this.grid.view;
39869         view.on("refresh", this.onViewChange, this);
39870         view.on("rowupdated", this.onRowUpdated, this);
39871         view.on("beforerowremoved", this.clearSelections, this);
39872         view.on("beforerowsinserted", this.clearSelections, this);
39873         if(this.grid.isEditor){
39874             this.grid.on("beforeedit", this.beforeEdit,  this);
39875         }
39876     },
39877
39878         //private
39879     beforeEdit : function(e){
39880         this.select(e.row, e.column, false, true, e.record);
39881     },
39882
39883         //private
39884     onRowUpdated : function(v, index, r){
39885         if(this.selection && this.selection.record == r){
39886             v.onCellSelect(index, this.selection.cell[1]);
39887         }
39888     },
39889
39890         //private
39891     onViewChange : function(){
39892         this.clearSelections(true);
39893     },
39894
39895         /**
39896          * Returns the currently selected cell,.
39897          * @return {Array} The selected cell (row, column) or null if none selected.
39898          */
39899     getSelectedCell : function(){
39900         return this.selection ? this.selection.cell : null;
39901     },
39902
39903     /**
39904      * Clears all selections.
39905      * @param {Boolean} true to prevent the gridview from being notified about the change.
39906      */
39907     clearSelections : function(preventNotify){
39908         var s = this.selection;
39909         if(s){
39910             if(preventNotify !== true){
39911                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39912             }
39913             this.selection = null;
39914             this.fireEvent("selectionchange", this, null);
39915         }
39916     },
39917
39918     /**
39919      * Returns true if there is a selection.
39920      * @return {Boolean}
39921      */
39922     hasSelection : function(){
39923         return this.selection ? true : false;
39924     },
39925
39926     /** @ignore */
39927     handleMouseDown : function(e, t){
39928         var v = this.grid.getView();
39929         if(this.isLocked()){
39930             return;
39931         };
39932         var row = v.findRowIndex(t);
39933         var cell = v.findCellIndex(t);
39934         if(row !== false && cell !== false){
39935             this.select(row, cell);
39936         }
39937     },
39938
39939     /**
39940      * Selects a cell.
39941      * @param {Number} rowIndex
39942      * @param {Number} collIndex
39943      */
39944     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39945         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39946             this.clearSelections();
39947             r = r || this.grid.dataSource.getAt(rowIndex);
39948             this.selection = {
39949                 record : r,
39950                 cell : [rowIndex, colIndex]
39951             };
39952             if(!preventViewNotify){
39953                 var v = this.grid.getView();
39954                 v.onCellSelect(rowIndex, colIndex);
39955                 if(preventFocus !== true){
39956                     v.focusCell(rowIndex, colIndex);
39957                 }
39958             }
39959             this.fireEvent("cellselect", this, rowIndex, colIndex);
39960             this.fireEvent("selectionchange", this, this.selection);
39961         }
39962     },
39963
39964         //private
39965     isSelectable : function(rowIndex, colIndex, cm){
39966         return !cm.isHidden(colIndex);
39967     },
39968
39969     /** @ignore */
39970     handleKeyDown : function(e){
39971         //Roo.log('Cell Sel Model handleKeyDown');
39972         if(!e.isNavKeyPress()){
39973             return;
39974         }
39975         var g = this.grid, s = this.selection;
39976         if(!s){
39977             e.stopEvent();
39978             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39979             if(cell){
39980                 this.select(cell[0], cell[1]);
39981             }
39982             return;
39983         }
39984         var sm = this;
39985         var walk = function(row, col, step){
39986             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39987         };
39988         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39989         var newCell;
39990
39991       
39992
39993         switch(k){
39994             case e.TAB:
39995                 // handled by onEditorKey
39996                 if (g.isEditor && g.editing) {
39997                     return;
39998                 }
39999                 if(e.shiftKey) {
40000                     newCell = walk(r, c-1, -1);
40001                 } else {
40002                     newCell = walk(r, c+1, 1);
40003                 }
40004                 break;
40005             
40006             case e.DOWN:
40007                newCell = walk(r+1, c, 1);
40008                 break;
40009             
40010             case e.UP:
40011                 newCell = walk(r-1, c, -1);
40012                 break;
40013             
40014             case e.RIGHT:
40015                 newCell = walk(r, c+1, 1);
40016                 break;
40017             
40018             case e.LEFT:
40019                 newCell = walk(r, c-1, -1);
40020                 break;
40021             
40022             case e.ENTER:
40023                 
40024                 if(g.isEditor && !g.editing){
40025                    g.startEditing(r, c);
40026                    e.stopEvent();
40027                    return;
40028                 }
40029                 
40030                 
40031              break;
40032         };
40033         if(newCell){
40034             this.select(newCell[0], newCell[1]);
40035             e.stopEvent();
40036             
40037         }
40038     },
40039
40040     acceptsNav : function(row, col, cm){
40041         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40042     },
40043     /**
40044      * Selects a cell.
40045      * @param {Number} field (not used) - as it's normally used as a listener
40046      * @param {Number} e - event - fake it by using
40047      *
40048      * var e = Roo.EventObjectImpl.prototype;
40049      * e.keyCode = e.TAB
40050      *
40051      * 
40052      */
40053     onEditorKey : function(field, e){
40054         
40055         var k = e.getKey(),
40056             newCell,
40057             g = this.grid,
40058             ed = g.activeEditor,
40059             forward = false;
40060         ///Roo.log('onEditorKey' + k);
40061         
40062         
40063         if (this.enter_is_tab && k == e.ENTER) {
40064             k = e.TAB;
40065         }
40066         
40067         if(k == e.TAB){
40068             if(e.shiftKey){
40069                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40070             }else{
40071                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40072                 forward = true;
40073             }
40074             
40075             e.stopEvent();
40076             
40077         } else if(k == e.ENTER &&  !e.ctrlKey){
40078             ed.completeEdit();
40079             e.stopEvent();
40080             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40081         
40082                 } else if(k == e.ESC){
40083             ed.cancelEdit();
40084         }
40085                 
40086         if (newCell) {
40087             var ecall = { cell : newCell, forward : forward };
40088             this.fireEvent('beforeeditnext', ecall );
40089             newCell = ecall.cell;
40090                         forward = ecall.forward;
40091         }
40092                 
40093         if(newCell){
40094             //Roo.log('next cell after edit');
40095             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40096         } else if (forward) {
40097             // tabbed past last
40098             this.fireEvent.defer(100, this, ['tabend',this]);
40099         }
40100     }
40101 });/*
40102  * Based on:
40103  * Ext JS Library 1.1.1
40104  * Copyright(c) 2006-2007, Ext JS, LLC.
40105  *
40106  * Originally Released Under LGPL - original licence link has changed is not relivant.
40107  *
40108  * Fork - LGPL
40109  * <script type="text/javascript">
40110  */
40111  
40112 /**
40113  * @class Roo.grid.EditorGrid
40114  * @extends Roo.grid.Grid
40115  * Class for creating and editable grid.
40116  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40117  * The container MUST have some type of size defined for the grid to fill. The container will be 
40118  * automatically set to position relative if it isn't already.
40119  * @param {Object} dataSource The data model to bind to
40120  * @param {Object} colModel The column model with info about this grid's columns
40121  */
40122 Roo.grid.EditorGrid = function(container, config){
40123     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40124     this.getGridEl().addClass("xedit-grid");
40125
40126     if(!this.selModel){
40127         this.selModel = new Roo.grid.CellSelectionModel();
40128     }
40129
40130     this.activeEditor = null;
40131
40132         this.addEvents({
40133             /**
40134              * @event beforeedit
40135              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40136              * <ul style="padding:5px;padding-left:16px;">
40137              * <li>grid - This grid</li>
40138              * <li>record - The record being edited</li>
40139              * <li>field - The field name being edited</li>
40140              * <li>value - The value for the field being edited.</li>
40141              * <li>row - The grid row index</li>
40142              * <li>column - The grid column index</li>
40143              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40144              * </ul>
40145              * @param {Object} e An edit event (see above for description)
40146              */
40147             "beforeedit" : true,
40148             /**
40149              * @event afteredit
40150              * Fires after a cell is edited. <br />
40151              * <ul style="padding:5px;padding-left:16px;">
40152              * <li>grid - This grid</li>
40153              * <li>record - The record being edited</li>
40154              * <li>field - The field name being edited</li>
40155              * <li>value - The value being set</li>
40156              * <li>originalValue - The original value for the field, before the edit.</li>
40157              * <li>row - The grid row index</li>
40158              * <li>column - The grid column index</li>
40159              * </ul>
40160              * @param {Object} e An edit event (see above for description)
40161              */
40162             "afteredit" : true,
40163             /**
40164              * @event validateedit
40165              * Fires after a cell is edited, but before the value is set in the record. 
40166          * You can use this to modify the value being set in the field, Return false
40167              * to cancel the change. The edit event object has the following properties <br />
40168              * <ul style="padding:5px;padding-left:16px;">
40169          * <li>editor - This editor</li>
40170              * <li>grid - This grid</li>
40171              * <li>record - The record being edited</li>
40172              * <li>field - The field name being edited</li>
40173              * <li>value - The value being set</li>
40174              * <li>originalValue - The original value for the field, before the edit.</li>
40175              * <li>row - The grid row index</li>
40176              * <li>column - The grid column index</li>
40177              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40178              * </ul>
40179              * @param {Object} e An edit event (see above for description)
40180              */
40181             "validateedit" : true
40182         });
40183     this.on("bodyscroll", this.stopEditing,  this);
40184     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40185 };
40186
40187 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40188     /**
40189      * @cfg {Number} clicksToEdit
40190      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40191      */
40192     clicksToEdit: 2,
40193
40194     // private
40195     isEditor : true,
40196     // private
40197     trackMouseOver: false, // causes very odd FF errors
40198
40199     onCellDblClick : function(g, row, col){
40200         this.startEditing(row, col);
40201     },
40202
40203     onEditComplete : function(ed, value, startValue){
40204         this.editing = false;
40205         this.activeEditor = null;
40206         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40207         var r = ed.record;
40208         var field = this.colModel.getDataIndex(ed.col);
40209         var e = {
40210             grid: this,
40211             record: r,
40212             field: field,
40213             originalValue: startValue,
40214             value: value,
40215             row: ed.row,
40216             column: ed.col,
40217             cancel:false,
40218             editor: ed
40219         };
40220         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40221         cell.show();
40222           
40223         if(String(value) !== String(startValue)){
40224             
40225             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40226                 r.set(field, e.value);
40227                 // if we are dealing with a combo box..
40228                 // then we also set the 'name' colum to be the displayField
40229                 if (ed.field.displayField && ed.field.name) {
40230                     r.set(ed.field.name, ed.field.el.dom.value);
40231                 }
40232                 
40233                 delete e.cancel; //?? why!!!
40234                 this.fireEvent("afteredit", e);
40235             }
40236         } else {
40237             this.fireEvent("afteredit", e); // always fire it!
40238         }
40239         this.view.focusCell(ed.row, ed.col);
40240     },
40241
40242     /**
40243      * Starts editing the specified for the specified row/column
40244      * @param {Number} rowIndex
40245      * @param {Number} colIndex
40246      */
40247     startEditing : function(row, col){
40248         this.stopEditing();
40249         if(this.colModel.isCellEditable(col, row)){
40250             this.view.ensureVisible(row, col, true);
40251           
40252             var r = this.dataSource.getAt(row);
40253             var field = this.colModel.getDataIndex(col);
40254             var cell = Roo.get(this.view.getCell(row,col));
40255             var e = {
40256                 grid: this,
40257                 record: r,
40258                 field: field,
40259                 value: r.data[field],
40260                 row: row,
40261                 column: col,
40262                 cancel:false 
40263             };
40264             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40265                 this.editing = true;
40266                 var ed = this.colModel.getCellEditor(col, row);
40267                 
40268                 if (!ed) {
40269                     return;
40270                 }
40271                 if(!ed.rendered){
40272                     ed.render(ed.parentEl || document.body);
40273                 }
40274                 ed.field.reset();
40275                
40276                 cell.hide();
40277                 
40278                 (function(){ // complex but required for focus issues in safari, ie and opera
40279                     ed.row = row;
40280                     ed.col = col;
40281                     ed.record = r;
40282                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40283                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40284                     this.activeEditor = ed;
40285                     var v = r.data[field];
40286                     ed.startEdit(this.view.getCell(row, col), v);
40287                     // combo's with 'displayField and name set
40288                     if (ed.field.displayField && ed.field.name) {
40289                         ed.field.el.dom.value = r.data[ed.field.name];
40290                     }
40291                     
40292                     
40293                 }).defer(50, this);
40294             }
40295         }
40296     },
40297         
40298     /**
40299      * Stops any active editing
40300      */
40301     stopEditing : function(){
40302         if(this.activeEditor){
40303             this.activeEditor.completeEdit();
40304         }
40305         this.activeEditor = null;
40306     },
40307         
40308          /**
40309      * Called to get grid's drag proxy text, by default returns this.ddText.
40310      * @return {String}
40311      */
40312     getDragDropText : function(){
40313         var count = this.selModel.getSelectedCell() ? 1 : 0;
40314         return String.format(this.ddText, count, count == 1 ? '' : 's');
40315     }
40316         
40317 });/*
40318  * Based on:
40319  * Ext JS Library 1.1.1
40320  * Copyright(c) 2006-2007, Ext JS, LLC.
40321  *
40322  * Originally Released Under LGPL - original licence link has changed is not relivant.
40323  *
40324  * Fork - LGPL
40325  * <script type="text/javascript">
40326  */
40327
40328 // private - not really -- you end up using it !
40329 // This is a support class used internally by the Grid components
40330
40331 /**
40332  * @class Roo.grid.GridEditor
40333  * @extends Roo.Editor
40334  * Class for creating and editable grid elements.
40335  * @param {Object} config any settings (must include field)
40336  */
40337 Roo.grid.GridEditor = function(field, config){
40338     if (!config && field.field) {
40339         config = field;
40340         field = Roo.factory(config.field, Roo.form);
40341     }
40342     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40343     field.monitorTab = false;
40344 };
40345
40346 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40347     
40348     /**
40349      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40350      */
40351     
40352     alignment: "tl-tl",
40353     autoSize: "width",
40354     hideEl : false,
40355     cls: "x-small-editor x-grid-editor",
40356     shim:false,
40357     shadow:"frame"
40358 });/*
40359  * Based on:
40360  * Ext JS Library 1.1.1
40361  * Copyright(c) 2006-2007, Ext JS, LLC.
40362  *
40363  * Originally Released Under LGPL - original licence link has changed is not relivant.
40364  *
40365  * Fork - LGPL
40366  * <script type="text/javascript">
40367  */
40368   
40369
40370   
40371 Roo.grid.PropertyRecord = Roo.data.Record.create([
40372     {name:'name',type:'string'},  'value'
40373 ]);
40374
40375
40376 Roo.grid.PropertyStore = function(grid, source){
40377     this.grid = grid;
40378     this.store = new Roo.data.Store({
40379         recordType : Roo.grid.PropertyRecord
40380     });
40381     this.store.on('update', this.onUpdate,  this);
40382     if(source){
40383         this.setSource(source);
40384     }
40385     Roo.grid.PropertyStore.superclass.constructor.call(this);
40386 };
40387
40388
40389
40390 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40391     setSource : function(o){
40392         this.source = o;
40393         this.store.removeAll();
40394         var data = [];
40395         for(var k in o){
40396             if(this.isEditableValue(o[k])){
40397                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40398             }
40399         }
40400         this.store.loadRecords({records: data}, {}, true);
40401     },
40402
40403     onUpdate : function(ds, record, type){
40404         if(type == Roo.data.Record.EDIT){
40405             var v = record.data['value'];
40406             var oldValue = record.modified['value'];
40407             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40408                 this.source[record.id] = v;
40409                 record.commit();
40410                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40411             }else{
40412                 record.reject();
40413             }
40414         }
40415     },
40416
40417     getProperty : function(row){
40418        return this.store.getAt(row);
40419     },
40420
40421     isEditableValue: function(val){
40422         if(val && val instanceof Date){
40423             return true;
40424         }else if(typeof val == 'object' || typeof val == 'function'){
40425             return false;
40426         }
40427         return true;
40428     },
40429
40430     setValue : function(prop, value){
40431         this.source[prop] = value;
40432         this.store.getById(prop).set('value', value);
40433     },
40434
40435     getSource : function(){
40436         return this.source;
40437     }
40438 });
40439
40440 Roo.grid.PropertyColumnModel = function(grid, store){
40441     this.grid = grid;
40442     var g = Roo.grid;
40443     g.PropertyColumnModel.superclass.constructor.call(this, [
40444         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40445         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40446     ]);
40447     this.store = store;
40448     this.bselect = Roo.DomHelper.append(document.body, {
40449         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40450             {tag: 'option', value: 'true', html: 'true'},
40451             {tag: 'option', value: 'false', html: 'false'}
40452         ]
40453     });
40454     Roo.id(this.bselect);
40455     var f = Roo.form;
40456     this.editors = {
40457         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40458         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40459         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40460         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40461         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40462     };
40463     this.renderCellDelegate = this.renderCell.createDelegate(this);
40464     this.renderPropDelegate = this.renderProp.createDelegate(this);
40465 };
40466
40467 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40468     
40469     
40470     nameText : 'Name',
40471     valueText : 'Value',
40472     
40473     dateFormat : 'm/j/Y',
40474     
40475     
40476     renderDate : function(dateVal){
40477         return dateVal.dateFormat(this.dateFormat);
40478     },
40479
40480     renderBool : function(bVal){
40481         return bVal ? 'true' : 'false';
40482     },
40483
40484     isCellEditable : function(colIndex, rowIndex){
40485         return colIndex == 1;
40486     },
40487
40488     getRenderer : function(col){
40489         return col == 1 ?
40490             this.renderCellDelegate : this.renderPropDelegate;
40491     },
40492
40493     renderProp : function(v){
40494         return this.getPropertyName(v);
40495     },
40496
40497     renderCell : function(val){
40498         var rv = val;
40499         if(val instanceof Date){
40500             rv = this.renderDate(val);
40501         }else if(typeof val == 'boolean'){
40502             rv = this.renderBool(val);
40503         }
40504         return Roo.util.Format.htmlEncode(rv);
40505     },
40506
40507     getPropertyName : function(name){
40508         var pn = this.grid.propertyNames;
40509         return pn && pn[name] ? pn[name] : name;
40510     },
40511
40512     getCellEditor : function(colIndex, rowIndex){
40513         var p = this.store.getProperty(rowIndex);
40514         var n = p.data['name'], val = p.data['value'];
40515         
40516         if(typeof(this.grid.customEditors[n]) == 'string'){
40517             return this.editors[this.grid.customEditors[n]];
40518         }
40519         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40520             return this.grid.customEditors[n];
40521         }
40522         if(val instanceof Date){
40523             return this.editors['date'];
40524         }else if(typeof val == 'number'){
40525             return this.editors['number'];
40526         }else if(typeof val == 'boolean'){
40527             return this.editors['boolean'];
40528         }else{
40529             return this.editors['string'];
40530         }
40531     }
40532 });
40533
40534 /**
40535  * @class Roo.grid.PropertyGrid
40536  * @extends Roo.grid.EditorGrid
40537  * This class represents the  interface of a component based property grid control.
40538  * <br><br>Usage:<pre><code>
40539  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40540       
40541  });
40542  // set any options
40543  grid.render();
40544  * </code></pre>
40545   
40546  * @constructor
40547  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40548  * The container MUST have some type of size defined for the grid to fill. The container will be
40549  * automatically set to position relative if it isn't already.
40550  * @param {Object} config A config object that sets properties on this grid.
40551  */
40552 Roo.grid.PropertyGrid = function(container, config){
40553     config = config || {};
40554     var store = new Roo.grid.PropertyStore(this);
40555     this.store = store;
40556     var cm = new Roo.grid.PropertyColumnModel(this, store);
40557     store.store.sort('name', 'ASC');
40558     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40559         ds: store.store,
40560         cm: cm,
40561         enableColLock:false,
40562         enableColumnMove:false,
40563         stripeRows:false,
40564         trackMouseOver: false,
40565         clicksToEdit:1
40566     }, config));
40567     this.getGridEl().addClass('x-props-grid');
40568     this.lastEditRow = null;
40569     this.on('columnresize', this.onColumnResize, this);
40570     this.addEvents({
40571          /**
40572              * @event beforepropertychange
40573              * Fires before a property changes (return false to stop?)
40574              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40575              * @param {String} id Record Id
40576              * @param {String} newval New Value
40577          * @param {String} oldval Old Value
40578              */
40579         "beforepropertychange": true,
40580         /**
40581              * @event propertychange
40582              * Fires after a property changes
40583              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40584              * @param {String} id Record Id
40585              * @param {String} newval New Value
40586          * @param {String} oldval Old Value
40587              */
40588         "propertychange": true
40589     });
40590     this.customEditors = this.customEditors || {};
40591 };
40592 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40593     
40594      /**
40595      * @cfg {Object} customEditors map of colnames=> custom editors.
40596      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40597      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40598      * false disables editing of the field.
40599          */
40600     
40601       /**
40602      * @cfg {Object} propertyNames map of property Names to their displayed value
40603          */
40604     
40605     render : function(){
40606         Roo.grid.PropertyGrid.superclass.render.call(this);
40607         this.autoSize.defer(100, this);
40608     },
40609
40610     autoSize : function(){
40611         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40612         if(this.view){
40613             this.view.fitColumns();
40614         }
40615     },
40616
40617     onColumnResize : function(){
40618         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40619         this.autoSize();
40620     },
40621     /**
40622      * Sets the data for the Grid
40623      * accepts a Key => Value object of all the elements avaiable.
40624      * @param {Object} data  to appear in grid.
40625      */
40626     setSource : function(source){
40627         this.store.setSource(source);
40628         //this.autoSize();
40629     },
40630     /**
40631      * Gets all the data from the grid.
40632      * @return {Object} data  data stored in grid
40633      */
40634     getSource : function(){
40635         return this.store.getSource();
40636     }
40637 });/*
40638   
40639  * Licence LGPL
40640  
40641  */
40642  
40643 /**
40644  * @class Roo.grid.Calendar
40645  * @extends Roo.util.Grid
40646  * This class extends the Grid to provide a calendar widget
40647  * <br><br>Usage:<pre><code>
40648  var grid = new Roo.grid.Calendar("my-container-id", {
40649      ds: myDataStore,
40650      cm: myColModel,
40651      selModel: mySelectionModel,
40652      autoSizeColumns: true,
40653      monitorWindowResize: false,
40654      trackMouseOver: true
40655      eventstore : real data store..
40656  });
40657  // set any options
40658  grid.render();
40659   
40660   * @constructor
40661  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40662  * The container MUST have some type of size defined for the grid to fill. The container will be
40663  * automatically set to position relative if it isn't already.
40664  * @param {Object} config A config object that sets properties on this grid.
40665  */
40666 Roo.grid.Calendar = function(container, config){
40667         // initialize the container
40668         this.container = Roo.get(container);
40669         this.container.update("");
40670         this.container.setStyle("overflow", "hidden");
40671     this.container.addClass('x-grid-container');
40672
40673     this.id = this.container.id;
40674
40675     Roo.apply(this, config);
40676     // check and correct shorthanded configs
40677     
40678     var rows = [];
40679     var d =1;
40680     for (var r = 0;r < 6;r++) {
40681         
40682         rows[r]=[];
40683         for (var c =0;c < 7;c++) {
40684             rows[r][c]= '';
40685         }
40686     }
40687     if (this.eventStore) {
40688         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40689         this.eventStore.on('load',this.onLoad, this);
40690         this.eventStore.on('beforeload',this.clearEvents, this);
40691          
40692     }
40693     
40694     this.dataSource = new Roo.data.Store({
40695             proxy: new Roo.data.MemoryProxy(rows),
40696             reader: new Roo.data.ArrayReader({}, [
40697                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40698     });
40699
40700     this.dataSource.load();
40701     this.ds = this.dataSource;
40702     this.ds.xmodule = this.xmodule || false;
40703     
40704     
40705     var cellRender = function(v,x,r)
40706     {
40707         return String.format(
40708             '<div class="fc-day  fc-widget-content"><div>' +
40709                 '<div class="fc-event-container"></div>' +
40710                 '<div class="fc-day-number">{0}</div>'+
40711                 
40712                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40713             '</div></div>', v);
40714     
40715     }
40716     
40717     
40718     this.colModel = new Roo.grid.ColumnModel( [
40719         {
40720             xtype: 'ColumnModel',
40721             xns: Roo.grid,
40722             dataIndex : 'weekday0',
40723             header : 'Sunday',
40724             renderer : cellRender
40725         },
40726         {
40727             xtype: 'ColumnModel',
40728             xns: Roo.grid,
40729             dataIndex : 'weekday1',
40730             header : 'Monday',
40731             renderer : cellRender
40732         },
40733         {
40734             xtype: 'ColumnModel',
40735             xns: Roo.grid,
40736             dataIndex : 'weekday2',
40737             header : 'Tuesday',
40738             renderer : cellRender
40739         },
40740         {
40741             xtype: 'ColumnModel',
40742             xns: Roo.grid,
40743             dataIndex : 'weekday3',
40744             header : 'Wednesday',
40745             renderer : cellRender
40746         },
40747         {
40748             xtype: 'ColumnModel',
40749             xns: Roo.grid,
40750             dataIndex : 'weekday4',
40751             header : 'Thursday',
40752             renderer : cellRender
40753         },
40754         {
40755             xtype: 'ColumnModel',
40756             xns: Roo.grid,
40757             dataIndex : 'weekday5',
40758             header : 'Friday',
40759             renderer : cellRender
40760         },
40761         {
40762             xtype: 'ColumnModel',
40763             xns: Roo.grid,
40764             dataIndex : 'weekday6',
40765             header : 'Saturday',
40766             renderer : cellRender
40767         }
40768     ]);
40769     this.cm = this.colModel;
40770     this.cm.xmodule = this.xmodule || false;
40771  
40772         
40773           
40774     //this.selModel = new Roo.grid.CellSelectionModel();
40775     //this.sm = this.selModel;
40776     //this.selModel.init(this);
40777     
40778     
40779     if(this.width){
40780         this.container.setWidth(this.width);
40781     }
40782
40783     if(this.height){
40784         this.container.setHeight(this.height);
40785     }
40786     /** @private */
40787         this.addEvents({
40788         // raw events
40789         /**
40790          * @event click
40791          * The raw click event for the entire grid.
40792          * @param {Roo.EventObject} e
40793          */
40794         "click" : true,
40795         /**
40796          * @event dblclick
40797          * The raw dblclick event for the entire grid.
40798          * @param {Roo.EventObject} e
40799          */
40800         "dblclick" : true,
40801         /**
40802          * @event contextmenu
40803          * The raw contextmenu event for the entire grid.
40804          * @param {Roo.EventObject} e
40805          */
40806         "contextmenu" : true,
40807         /**
40808          * @event mousedown
40809          * The raw mousedown event for the entire grid.
40810          * @param {Roo.EventObject} e
40811          */
40812         "mousedown" : true,
40813         /**
40814          * @event mouseup
40815          * The raw mouseup event for the entire grid.
40816          * @param {Roo.EventObject} e
40817          */
40818         "mouseup" : true,
40819         /**
40820          * @event mouseover
40821          * The raw mouseover event for the entire grid.
40822          * @param {Roo.EventObject} e
40823          */
40824         "mouseover" : true,
40825         /**
40826          * @event mouseout
40827          * The raw mouseout event for the entire grid.
40828          * @param {Roo.EventObject} e
40829          */
40830         "mouseout" : true,
40831         /**
40832          * @event keypress
40833          * The raw keypress event for the entire grid.
40834          * @param {Roo.EventObject} e
40835          */
40836         "keypress" : true,
40837         /**
40838          * @event keydown
40839          * The raw keydown event for the entire grid.
40840          * @param {Roo.EventObject} e
40841          */
40842         "keydown" : true,
40843
40844         // custom events
40845
40846         /**
40847          * @event cellclick
40848          * Fires when a cell is clicked
40849          * @param {Grid} this
40850          * @param {Number} rowIndex
40851          * @param {Number} columnIndex
40852          * @param {Roo.EventObject} e
40853          */
40854         "cellclick" : true,
40855         /**
40856          * @event celldblclick
40857          * Fires when a cell is double clicked
40858          * @param {Grid} this
40859          * @param {Number} rowIndex
40860          * @param {Number} columnIndex
40861          * @param {Roo.EventObject} e
40862          */
40863         "celldblclick" : true,
40864         /**
40865          * @event rowclick
40866          * Fires when a row is clicked
40867          * @param {Grid} this
40868          * @param {Number} rowIndex
40869          * @param {Roo.EventObject} e
40870          */
40871         "rowclick" : true,
40872         /**
40873          * @event rowdblclick
40874          * Fires when a row is double clicked
40875          * @param {Grid} this
40876          * @param {Number} rowIndex
40877          * @param {Roo.EventObject} e
40878          */
40879         "rowdblclick" : true,
40880         /**
40881          * @event headerclick
40882          * Fires when a header is clicked
40883          * @param {Grid} this
40884          * @param {Number} columnIndex
40885          * @param {Roo.EventObject} e
40886          */
40887         "headerclick" : true,
40888         /**
40889          * @event headerdblclick
40890          * Fires when a header cell is double clicked
40891          * @param {Grid} this
40892          * @param {Number} columnIndex
40893          * @param {Roo.EventObject} e
40894          */
40895         "headerdblclick" : true,
40896         /**
40897          * @event rowcontextmenu
40898          * Fires when a row is right clicked
40899          * @param {Grid} this
40900          * @param {Number} rowIndex
40901          * @param {Roo.EventObject} e
40902          */
40903         "rowcontextmenu" : true,
40904         /**
40905          * @event cellcontextmenu
40906          * Fires when a cell is right clicked
40907          * @param {Grid} this
40908          * @param {Number} rowIndex
40909          * @param {Number} cellIndex
40910          * @param {Roo.EventObject} e
40911          */
40912          "cellcontextmenu" : true,
40913         /**
40914          * @event headercontextmenu
40915          * Fires when a header is right clicked
40916          * @param {Grid} this
40917          * @param {Number} columnIndex
40918          * @param {Roo.EventObject} e
40919          */
40920         "headercontextmenu" : true,
40921         /**
40922          * @event bodyscroll
40923          * Fires when the body element is scrolled
40924          * @param {Number} scrollLeft
40925          * @param {Number} scrollTop
40926          */
40927         "bodyscroll" : true,
40928         /**
40929          * @event columnresize
40930          * Fires when the user resizes a column
40931          * @param {Number} columnIndex
40932          * @param {Number} newSize
40933          */
40934         "columnresize" : true,
40935         /**
40936          * @event columnmove
40937          * Fires when the user moves a column
40938          * @param {Number} oldIndex
40939          * @param {Number} newIndex
40940          */
40941         "columnmove" : true,
40942         /**
40943          * @event startdrag
40944          * Fires when row(s) start being dragged
40945          * @param {Grid} this
40946          * @param {Roo.GridDD} dd The drag drop object
40947          * @param {event} e The raw browser event
40948          */
40949         "startdrag" : true,
40950         /**
40951          * @event enddrag
40952          * Fires when a drag operation is complete
40953          * @param {Grid} this
40954          * @param {Roo.GridDD} dd The drag drop object
40955          * @param {event} e The raw browser event
40956          */
40957         "enddrag" : true,
40958         /**
40959          * @event dragdrop
40960          * Fires when dragged row(s) are dropped on a valid DD target
40961          * @param {Grid} this
40962          * @param {Roo.GridDD} dd The drag drop object
40963          * @param {String} targetId The target drag drop object
40964          * @param {event} e The raw browser event
40965          */
40966         "dragdrop" : true,
40967         /**
40968          * @event dragover
40969          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40970          * @param {Grid} this
40971          * @param {Roo.GridDD} dd The drag drop object
40972          * @param {String} targetId The target drag drop object
40973          * @param {event} e The raw browser event
40974          */
40975         "dragover" : true,
40976         /**
40977          * @event dragenter
40978          *  Fires when the dragged row(s) first cross another DD target while being dragged
40979          * @param {Grid} this
40980          * @param {Roo.GridDD} dd The drag drop object
40981          * @param {String} targetId The target drag drop object
40982          * @param {event} e The raw browser event
40983          */
40984         "dragenter" : true,
40985         /**
40986          * @event dragout
40987          * Fires when the dragged row(s) leave another DD target while being dragged
40988          * @param {Grid} this
40989          * @param {Roo.GridDD} dd The drag drop object
40990          * @param {String} targetId The target drag drop object
40991          * @param {event} e The raw browser event
40992          */
40993         "dragout" : true,
40994         /**
40995          * @event rowclass
40996          * Fires when a row is rendered, so you can change add a style to it.
40997          * @param {GridView} gridview   The grid view
40998          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40999          */
41000         'rowclass' : true,
41001
41002         /**
41003          * @event render
41004          * Fires when the grid is rendered
41005          * @param {Grid} grid
41006          */
41007         'render' : true,
41008             /**
41009              * @event select
41010              * Fires when a date is selected
41011              * @param {DatePicker} this
41012              * @param {Date} date The selected date
41013              */
41014         'select': true,
41015         /**
41016              * @event monthchange
41017              * Fires when the displayed month changes 
41018              * @param {DatePicker} this
41019              * @param {Date} date The selected month
41020              */
41021         'monthchange': true,
41022         /**
41023              * @event evententer
41024              * Fires when mouse over an event
41025              * @param {Calendar} this
41026              * @param {event} Event
41027              */
41028         'evententer': true,
41029         /**
41030              * @event eventleave
41031              * Fires when the mouse leaves an
41032              * @param {Calendar} this
41033              * @param {event}
41034              */
41035         'eventleave': true,
41036         /**
41037              * @event eventclick
41038              * Fires when the mouse click an
41039              * @param {Calendar} this
41040              * @param {event}
41041              */
41042         'eventclick': true,
41043         /**
41044              * @event eventrender
41045              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41046              * @param {Calendar} this
41047              * @param {data} data to be modified
41048              */
41049         'eventrender': true
41050         
41051     });
41052
41053     Roo.grid.Grid.superclass.constructor.call(this);
41054     this.on('render', function() {
41055         this.view.el.addClass('x-grid-cal'); 
41056         
41057         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41058
41059     },this);
41060     
41061     if (!Roo.grid.Calendar.style) {
41062         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41063             
41064             
41065             '.x-grid-cal .x-grid-col' :  {
41066                 height: 'auto !important',
41067                 'vertical-align': 'top'
41068             },
41069             '.x-grid-cal  .fc-event-hori' : {
41070                 height: '14px'
41071             }
41072              
41073             
41074         }, Roo.id());
41075     }
41076
41077     
41078     
41079 };
41080 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41081     /**
41082      * @cfg {Store} eventStore The store that loads events.
41083      */
41084     eventStore : 25,
41085
41086      
41087     activeDate : false,
41088     startDay : 0,
41089     autoWidth : true,
41090     monitorWindowResize : false,
41091
41092     
41093     resizeColumns : function() {
41094         var col = (this.view.el.getWidth() / 7) - 3;
41095         // loop through cols, and setWidth
41096         for(var i =0 ; i < 7 ; i++){
41097             this.cm.setColumnWidth(i, col);
41098         }
41099     },
41100      setDate :function(date) {
41101         
41102         Roo.log('setDate?');
41103         
41104         this.resizeColumns();
41105         var vd = this.activeDate;
41106         this.activeDate = date;
41107 //        if(vd && this.el){
41108 //            var t = date.getTime();
41109 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41110 //                Roo.log('using add remove');
41111 //                
41112 //                this.fireEvent('monthchange', this, date);
41113 //                
41114 //                this.cells.removeClass("fc-state-highlight");
41115 //                this.cells.each(function(c){
41116 //                   if(c.dateValue == t){
41117 //                       c.addClass("fc-state-highlight");
41118 //                       setTimeout(function(){
41119 //                            try{c.dom.firstChild.focus();}catch(e){}
41120 //                       }, 50);
41121 //                       return false;
41122 //                   }
41123 //                   return true;
41124 //                });
41125 //                return;
41126 //            }
41127 //        }
41128         
41129         var days = date.getDaysInMonth();
41130         
41131         var firstOfMonth = date.getFirstDateOfMonth();
41132         var startingPos = firstOfMonth.getDay()-this.startDay;
41133         
41134         if(startingPos < this.startDay){
41135             startingPos += 7;
41136         }
41137         
41138         var pm = date.add(Date.MONTH, -1);
41139         var prevStart = pm.getDaysInMonth()-startingPos;
41140 //        
41141         
41142         
41143         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41144         
41145         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41146         //this.cells.addClassOnOver('fc-state-hover');
41147         
41148         var cells = this.cells.elements;
41149         var textEls = this.textNodes;
41150         
41151         //Roo.each(cells, function(cell){
41152         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41153         //});
41154         
41155         days += startingPos;
41156
41157         // convert everything to numbers so it's fast
41158         var day = 86400000;
41159         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41160         //Roo.log(d);
41161         //Roo.log(pm);
41162         //Roo.log(prevStart);
41163         
41164         var today = new Date().clearTime().getTime();
41165         var sel = date.clearTime().getTime();
41166         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41167         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41168         var ddMatch = this.disabledDatesRE;
41169         var ddText = this.disabledDatesText;
41170         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41171         var ddaysText = this.disabledDaysText;
41172         var format = this.format;
41173         
41174         var setCellClass = function(cal, cell){
41175             
41176             //Roo.log('set Cell Class');
41177             cell.title = "";
41178             var t = d.getTime();
41179             
41180             //Roo.log(d);
41181             
41182             
41183             cell.dateValue = t;
41184             if(t == today){
41185                 cell.className += " fc-today";
41186                 cell.className += " fc-state-highlight";
41187                 cell.title = cal.todayText;
41188             }
41189             if(t == sel){
41190                 // disable highlight in other month..
41191                 cell.className += " fc-state-highlight";
41192                 
41193             }
41194             // disabling
41195             if(t < min) {
41196                 //cell.className = " fc-state-disabled";
41197                 cell.title = cal.minText;
41198                 return;
41199             }
41200             if(t > max) {
41201                 //cell.className = " fc-state-disabled";
41202                 cell.title = cal.maxText;
41203                 return;
41204             }
41205             if(ddays){
41206                 if(ddays.indexOf(d.getDay()) != -1){
41207                     // cell.title = ddaysText;
41208                    // cell.className = " fc-state-disabled";
41209                 }
41210             }
41211             if(ddMatch && format){
41212                 var fvalue = d.dateFormat(format);
41213                 if(ddMatch.test(fvalue)){
41214                     cell.title = ddText.replace("%0", fvalue);
41215                    cell.className = " fc-state-disabled";
41216                 }
41217             }
41218             
41219             if (!cell.initialClassName) {
41220                 cell.initialClassName = cell.dom.className;
41221             }
41222             
41223             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41224         };
41225
41226         var i = 0;
41227         
41228         for(; i < startingPos; i++) {
41229             cells[i].dayName =  (++prevStart);
41230             Roo.log(textEls[i]);
41231             d.setDate(d.getDate()+1);
41232             
41233             //cells[i].className = "fc-past fc-other-month";
41234             setCellClass(this, cells[i]);
41235         }
41236         
41237         var intDay = 0;
41238         
41239         for(; i < days; i++){
41240             intDay = i - startingPos + 1;
41241             cells[i].dayName =  (intDay);
41242             d.setDate(d.getDate()+1);
41243             
41244             cells[i].className = ''; // "x-date-active";
41245             setCellClass(this, cells[i]);
41246         }
41247         var extraDays = 0;
41248         
41249         for(; i < 42; i++) {
41250             //textEls[i].innerHTML = (++extraDays);
41251             
41252             d.setDate(d.getDate()+1);
41253             cells[i].dayName = (++extraDays);
41254             cells[i].className = "fc-future fc-other-month";
41255             setCellClass(this, cells[i]);
41256         }
41257         
41258         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41259         
41260         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41261         
41262         // this will cause all the cells to mis
41263         var rows= [];
41264         var i =0;
41265         for (var r = 0;r < 6;r++) {
41266             for (var c =0;c < 7;c++) {
41267                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41268             }    
41269         }
41270         
41271         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41272         for(i=0;i<cells.length;i++) {
41273             
41274             this.cells.elements[i].dayName = cells[i].dayName ;
41275             this.cells.elements[i].className = cells[i].className;
41276             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41277             this.cells.elements[i].title = cells[i].title ;
41278             this.cells.elements[i].dateValue = cells[i].dateValue ;
41279         }
41280         
41281         
41282         
41283         
41284         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41285         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41286         
41287         ////if(totalRows != 6){
41288             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41289            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41290        // }
41291         
41292         this.fireEvent('monthchange', this, date);
41293         
41294         
41295     },
41296  /**
41297      * Returns the grid's SelectionModel.
41298      * @return {SelectionModel}
41299      */
41300     getSelectionModel : function(){
41301         if(!this.selModel){
41302             this.selModel = new Roo.grid.CellSelectionModel();
41303         }
41304         return this.selModel;
41305     },
41306
41307     load: function() {
41308         this.eventStore.load()
41309         
41310         
41311         
41312     },
41313     
41314     findCell : function(dt) {
41315         dt = dt.clearTime().getTime();
41316         var ret = false;
41317         this.cells.each(function(c){
41318             //Roo.log("check " +c.dateValue + '?=' + dt);
41319             if(c.dateValue == dt){
41320                 ret = c;
41321                 return false;
41322             }
41323             return true;
41324         });
41325         
41326         return ret;
41327     },
41328     
41329     findCells : function(rec) {
41330         var s = rec.data.start_dt.clone().clearTime().getTime();
41331        // Roo.log(s);
41332         var e= rec.data.end_dt.clone().clearTime().getTime();
41333        // Roo.log(e);
41334         var ret = [];
41335         this.cells.each(function(c){
41336              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41337             
41338             if(c.dateValue > e){
41339                 return ;
41340             }
41341             if(c.dateValue < s){
41342                 return ;
41343             }
41344             ret.push(c);
41345         });
41346         
41347         return ret;    
41348     },
41349     
41350     findBestRow: function(cells)
41351     {
41352         var ret = 0;
41353         
41354         for (var i =0 ; i < cells.length;i++) {
41355             ret  = Math.max(cells[i].rows || 0,ret);
41356         }
41357         return ret;
41358         
41359     },
41360     
41361     
41362     addItem : function(rec)
41363     {
41364         // look for vertical location slot in
41365         var cells = this.findCells(rec);
41366         
41367         rec.row = this.findBestRow(cells);
41368         
41369         // work out the location.
41370         
41371         var crow = false;
41372         var rows = [];
41373         for(var i =0; i < cells.length; i++) {
41374             if (!crow) {
41375                 crow = {
41376                     start : cells[i],
41377                     end :  cells[i]
41378                 };
41379                 continue;
41380             }
41381             if (crow.start.getY() == cells[i].getY()) {
41382                 // on same row.
41383                 crow.end = cells[i];
41384                 continue;
41385             }
41386             // different row.
41387             rows.push(crow);
41388             crow = {
41389                 start: cells[i],
41390                 end : cells[i]
41391             };
41392             
41393         }
41394         
41395         rows.push(crow);
41396         rec.els = [];
41397         rec.rows = rows;
41398         rec.cells = cells;
41399         for (var i = 0; i < cells.length;i++) {
41400             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41401             
41402         }
41403         
41404         
41405     },
41406     
41407     clearEvents: function() {
41408         
41409         if (!this.eventStore.getCount()) {
41410             return;
41411         }
41412         // reset number of rows in cells.
41413         Roo.each(this.cells.elements, function(c){
41414             c.rows = 0;
41415         });
41416         
41417         this.eventStore.each(function(e) {
41418             this.clearEvent(e);
41419         },this);
41420         
41421     },
41422     
41423     clearEvent : function(ev)
41424     {
41425         if (ev.els) {
41426             Roo.each(ev.els, function(el) {
41427                 el.un('mouseenter' ,this.onEventEnter, this);
41428                 el.un('mouseleave' ,this.onEventLeave, this);
41429                 el.remove();
41430             },this);
41431             ev.els = [];
41432         }
41433     },
41434     
41435     
41436     renderEvent : function(ev,ctr) {
41437         if (!ctr) {
41438              ctr = this.view.el.select('.fc-event-container',true).first();
41439         }
41440         
41441          
41442         this.clearEvent(ev);
41443             //code
41444        
41445         
41446         
41447         ev.els = [];
41448         var cells = ev.cells;
41449         var rows = ev.rows;
41450         this.fireEvent('eventrender', this, ev);
41451         
41452         for(var i =0; i < rows.length; i++) {
41453             
41454             cls = '';
41455             if (i == 0) {
41456                 cls += ' fc-event-start';
41457             }
41458             if ((i+1) == rows.length) {
41459                 cls += ' fc-event-end';
41460             }
41461             
41462             //Roo.log(ev.data);
41463             // how many rows should it span..
41464             var cg = this.eventTmpl.append(ctr,Roo.apply({
41465                 fccls : cls
41466                 
41467             }, ev.data) , true);
41468             
41469             
41470             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41471             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41472             cg.on('click', this.onEventClick, this, ev);
41473             
41474             ev.els.push(cg);
41475             
41476             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41477             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41478             //Roo.log(cg);
41479              
41480             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41481             cg.setWidth(ebox.right - sbox.x -2);
41482         }
41483     },
41484     
41485     renderEvents: function()
41486     {   
41487         // first make sure there is enough space..
41488         
41489         if (!this.eventTmpl) {
41490             this.eventTmpl = new Roo.Template(
41491                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41492                     '<div class="fc-event-inner">' +
41493                         '<span class="fc-event-time">{time}</span>' +
41494                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41495                     '</div>' +
41496                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41497                 '</div>'
41498             );
41499                 
41500         }
41501                
41502         
41503         
41504         this.cells.each(function(c) {
41505             //Roo.log(c.select('.fc-day-content div',true).first());
41506             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41507         });
41508         
41509         var ctr = this.view.el.select('.fc-event-container',true).first();
41510         
41511         var cls;
41512         this.eventStore.each(function(ev){
41513             
41514             this.renderEvent(ev);
41515              
41516              
41517         }, this);
41518         this.view.layout();
41519         
41520     },
41521     
41522     onEventEnter: function (e, el,event,d) {
41523         this.fireEvent('evententer', this, el, event);
41524     },
41525     
41526     onEventLeave: function (e, el,event,d) {
41527         this.fireEvent('eventleave', this, el, event);
41528     },
41529     
41530     onEventClick: function (e, el,event,d) {
41531         this.fireEvent('eventclick', this, el, event);
41532     },
41533     
41534     onMonthChange: function () {
41535         this.store.load();
41536     },
41537     
41538     onLoad: function () {
41539         
41540         //Roo.log('calendar onload');
41541 //         
41542         if(this.eventStore.getCount() > 0){
41543             
41544            
41545             
41546             this.eventStore.each(function(d){
41547                 
41548                 
41549                 // FIXME..
41550                 var add =   d.data;
41551                 if (typeof(add.end_dt) == 'undefined')  {
41552                     Roo.log("Missing End time in calendar data: ");
41553                     Roo.log(d);
41554                     return;
41555                 }
41556                 if (typeof(add.start_dt) == 'undefined')  {
41557                     Roo.log("Missing Start time in calendar data: ");
41558                     Roo.log(d);
41559                     return;
41560                 }
41561                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41562                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41563                 add.id = add.id || d.id;
41564                 add.title = add.title || '??';
41565                 
41566                 this.addItem(d);
41567                 
41568              
41569             },this);
41570         }
41571         
41572         this.renderEvents();
41573     }
41574     
41575
41576 });
41577 /*
41578  grid : {
41579                 xtype: 'Grid',
41580                 xns: Roo.grid,
41581                 listeners : {
41582                     render : function ()
41583                     {
41584                         _this.grid = this;
41585                         
41586                         if (!this.view.el.hasClass('course-timesheet')) {
41587                             this.view.el.addClass('course-timesheet');
41588                         }
41589                         if (this.tsStyle) {
41590                             this.ds.load({});
41591                             return; 
41592                         }
41593                         Roo.log('width');
41594                         Roo.log(_this.grid.view.el.getWidth());
41595                         
41596                         
41597                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41598                             '.course-timesheet .x-grid-row' : {
41599                                 height: '80px'
41600                             },
41601                             '.x-grid-row td' : {
41602                                 'vertical-align' : 0
41603                             },
41604                             '.course-edit-link' : {
41605                                 'color' : 'blue',
41606                                 'text-overflow' : 'ellipsis',
41607                                 'overflow' : 'hidden',
41608                                 'white-space' : 'nowrap',
41609                                 'cursor' : 'pointer'
41610                             },
41611                             '.sub-link' : {
41612                                 'color' : 'green'
41613                             },
41614                             '.de-act-sup-link' : {
41615                                 'color' : 'purple',
41616                                 'text-decoration' : 'line-through'
41617                             },
41618                             '.de-act-link' : {
41619                                 'color' : 'red',
41620                                 'text-decoration' : 'line-through'
41621                             },
41622                             '.course-timesheet .course-highlight' : {
41623                                 'border-top-style': 'dashed !important',
41624                                 'border-bottom-bottom': 'dashed !important'
41625                             },
41626                             '.course-timesheet .course-item' : {
41627                                 'font-family'   : 'tahoma, arial, helvetica',
41628                                 'font-size'     : '11px',
41629                                 'overflow'      : 'hidden',
41630                                 'padding-left'  : '10px',
41631                                 'padding-right' : '10px',
41632                                 'padding-top' : '10px' 
41633                             }
41634                             
41635                         }, Roo.id());
41636                                 this.ds.load({});
41637                     }
41638                 },
41639                 autoWidth : true,
41640                 monitorWindowResize : false,
41641                 cellrenderer : function(v,x,r)
41642                 {
41643                     return v;
41644                 },
41645                 sm : {
41646                     xtype: 'CellSelectionModel',
41647                     xns: Roo.grid
41648                 },
41649                 dataSource : {
41650                     xtype: 'Store',
41651                     xns: Roo.data,
41652                     listeners : {
41653                         beforeload : function (_self, options)
41654                         {
41655                             options.params = options.params || {};
41656                             options.params._month = _this.monthField.getValue();
41657                             options.params.limit = 9999;
41658                             options.params['sort'] = 'when_dt';    
41659                             options.params['dir'] = 'ASC';    
41660                             this.proxy.loadResponse = this.loadResponse;
41661                             Roo.log("load?");
41662                             //this.addColumns();
41663                         },
41664                         load : function (_self, records, options)
41665                         {
41666                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41667                                 // if you click on the translation.. you can edit it...
41668                                 var el = Roo.get(this);
41669                                 var id = el.dom.getAttribute('data-id');
41670                                 var d = el.dom.getAttribute('data-date');
41671                                 var t = el.dom.getAttribute('data-time');
41672                                 //var id = this.child('span').dom.textContent;
41673                                 
41674                                 //Roo.log(this);
41675                                 Pman.Dialog.CourseCalendar.show({
41676                                     id : id,
41677                                     when_d : d,
41678                                     when_t : t,
41679                                     productitem_active : id ? 1 : 0
41680                                 }, function() {
41681                                     _this.grid.ds.load({});
41682                                 });
41683                            
41684                            });
41685                            
41686                            _this.panel.fireEvent('resize', [ '', '' ]);
41687                         }
41688                     },
41689                     loadResponse : function(o, success, response){
41690                             // this is overridden on before load..
41691                             
41692                             Roo.log("our code?");       
41693                             //Roo.log(success);
41694                             //Roo.log(response)
41695                             delete this.activeRequest;
41696                             if(!success){
41697                                 this.fireEvent("loadexception", this, o, response);
41698                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41699                                 return;
41700                             }
41701                             var result;
41702                             try {
41703                                 result = o.reader.read(response);
41704                             }catch(e){
41705                                 Roo.log("load exception?");
41706                                 this.fireEvent("loadexception", this, o, response, e);
41707                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41708                                 return;
41709                             }
41710                             Roo.log("ready...");        
41711                             // loop through result.records;
41712                             // and set this.tdate[date] = [] << array of records..
41713                             _this.tdata  = {};
41714                             Roo.each(result.records, function(r){
41715                                 //Roo.log(r.data);
41716                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41717                                     _this.tdata[r.data.when_dt.format('j')] = [];
41718                                 }
41719                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41720                             });
41721                             
41722                             //Roo.log(_this.tdata);
41723                             
41724                             result.records = [];
41725                             result.totalRecords = 6;
41726                     
41727                             // let's generate some duumy records for the rows.
41728                             //var st = _this.dateField.getValue();
41729                             
41730                             // work out monday..
41731                             //st = st.add(Date.DAY, -1 * st.format('w'));
41732                             
41733                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41734                             
41735                             var firstOfMonth = date.getFirstDayOfMonth();
41736                             var days = date.getDaysInMonth();
41737                             var d = 1;
41738                             var firstAdded = false;
41739                             for (var i = 0; i < result.totalRecords ; i++) {
41740                                 //var d= st.add(Date.DAY, i);
41741                                 var row = {};
41742                                 var added = 0;
41743                                 for(var w = 0 ; w < 7 ; w++){
41744                                     if(!firstAdded && firstOfMonth != w){
41745                                         continue;
41746                                     }
41747                                     if(d > days){
41748                                         continue;
41749                                     }
41750                                     firstAdded = true;
41751                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41752                                     row['weekday'+w] = String.format(
41753                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41754                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41755                                                     d,
41756                                                     date.format('Y-m-')+dd
41757                                                 );
41758                                     added++;
41759                                     if(typeof(_this.tdata[d]) != 'undefined'){
41760                                         Roo.each(_this.tdata[d], function(r){
41761                                             var is_sub = '';
41762                                             var deactive = '';
41763                                             var id = r.id;
41764                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41765                                             if(r.parent_id*1>0){
41766                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41767                                                 id = r.parent_id;
41768                                             }
41769                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41770                                                 deactive = 'de-act-link';
41771                                             }
41772                                             
41773                                             row['weekday'+w] += String.format(
41774                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41775                                                     id, //0
41776                                                     r.product_id_name, //1
41777                                                     r.when_dt.format('h:ia'), //2
41778                                                     is_sub, //3
41779                                                     deactive, //4
41780                                                     desc // 5
41781                                             );
41782                                         });
41783                                     }
41784                                     d++;
41785                                 }
41786                                 
41787                                 // only do this if something added..
41788                                 if(added > 0){ 
41789                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41790                                 }
41791                                 
41792                                 
41793                                 // push it twice. (second one with an hour..
41794                                 
41795                             }
41796                             //Roo.log(result);
41797                             this.fireEvent("load", this, o, o.request.arg);
41798                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41799                         },
41800                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41801                     proxy : {
41802                         xtype: 'HttpProxy',
41803                         xns: Roo.data,
41804                         method : 'GET',
41805                         url : baseURL + '/Roo/Shop_course.php'
41806                     },
41807                     reader : {
41808                         xtype: 'JsonReader',
41809                         xns: Roo.data,
41810                         id : 'id',
41811                         fields : [
41812                             {
41813                                 'name': 'id',
41814                                 'type': 'int'
41815                             },
41816                             {
41817                                 'name': 'when_dt',
41818                                 'type': 'string'
41819                             },
41820                             {
41821                                 'name': 'end_dt',
41822                                 'type': 'string'
41823                             },
41824                             {
41825                                 'name': 'parent_id',
41826                                 'type': 'int'
41827                             },
41828                             {
41829                                 'name': 'product_id',
41830                                 'type': 'int'
41831                             },
41832                             {
41833                                 'name': 'productitem_id',
41834                                 'type': 'int'
41835                             },
41836                             {
41837                                 'name': 'guid',
41838                                 'type': 'int'
41839                             }
41840                         ]
41841                     }
41842                 },
41843                 toolbar : {
41844                     xtype: 'Toolbar',
41845                     xns: Roo,
41846                     items : [
41847                         {
41848                             xtype: 'Button',
41849                             xns: Roo.Toolbar,
41850                             listeners : {
41851                                 click : function (_self, e)
41852                                 {
41853                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41854                                     sd.setMonth(sd.getMonth()-1);
41855                                     _this.monthField.setValue(sd.format('Y-m-d'));
41856                                     _this.grid.ds.load({});
41857                                 }
41858                             },
41859                             text : "Back"
41860                         },
41861                         {
41862                             xtype: 'Separator',
41863                             xns: Roo.Toolbar
41864                         },
41865                         {
41866                             xtype: 'MonthField',
41867                             xns: Roo.form,
41868                             listeners : {
41869                                 render : function (_self)
41870                                 {
41871                                     _this.monthField = _self;
41872                                    // _this.monthField.set  today
41873                                 },
41874                                 select : function (combo, date)
41875                                 {
41876                                     _this.grid.ds.load({});
41877                                 }
41878                             },
41879                             value : (function() { return new Date(); })()
41880                         },
41881                         {
41882                             xtype: 'Separator',
41883                             xns: Roo.Toolbar
41884                         },
41885                         {
41886                             xtype: 'TextItem',
41887                             xns: Roo.Toolbar,
41888                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41889                         },
41890                         {
41891                             xtype: 'Fill',
41892                             xns: Roo.Toolbar
41893                         },
41894                         {
41895                             xtype: 'Button',
41896                             xns: Roo.Toolbar,
41897                             listeners : {
41898                                 click : function (_self, e)
41899                                 {
41900                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41901                                     sd.setMonth(sd.getMonth()+1);
41902                                     _this.monthField.setValue(sd.format('Y-m-d'));
41903                                     _this.grid.ds.load({});
41904                                 }
41905                             },
41906                             text : "Next"
41907                         }
41908                     ]
41909                 },
41910                  
41911             }
41912         };
41913         
41914         *//*
41915  * Based on:
41916  * Ext JS Library 1.1.1
41917  * Copyright(c) 2006-2007, Ext JS, LLC.
41918  *
41919  * Originally Released Under LGPL - original licence link has changed is not relivant.
41920  *
41921  * Fork - LGPL
41922  * <script type="text/javascript">
41923  */
41924  
41925 /**
41926  * @class Roo.LoadMask
41927  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41928  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41929  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41930  * element's UpdateManager load indicator and will be destroyed after the initial load.
41931  * @constructor
41932  * Create a new LoadMask
41933  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41934  * @param {Object} config The config object
41935  */
41936 Roo.LoadMask = function(el, config){
41937     this.el = Roo.get(el);
41938     Roo.apply(this, config);
41939     if(this.store){
41940         this.store.on('beforeload', this.onBeforeLoad, this);
41941         this.store.on('load', this.onLoad, this);
41942         this.store.on('loadexception', this.onLoadException, this);
41943         this.removeMask = false;
41944     }else{
41945         var um = this.el.getUpdateManager();
41946         um.showLoadIndicator = false; // disable the default indicator
41947         um.on('beforeupdate', this.onBeforeLoad, this);
41948         um.on('update', this.onLoad, this);
41949         um.on('failure', this.onLoad, this);
41950         this.removeMask = true;
41951     }
41952 };
41953
41954 Roo.LoadMask.prototype = {
41955     /**
41956      * @cfg {Boolean} removeMask
41957      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41958      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41959      */
41960     /**
41961      * @cfg {String} msg
41962      * The text to display in a centered loading message box (defaults to 'Loading...')
41963      */
41964     msg : 'Loading...',
41965     /**
41966      * @cfg {String} msgCls
41967      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41968      */
41969     msgCls : 'x-mask-loading',
41970
41971     /**
41972      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41973      * @type Boolean
41974      */
41975     disabled: false,
41976
41977     /**
41978      * Disables the mask to prevent it from being displayed
41979      */
41980     disable : function(){
41981        this.disabled = true;
41982     },
41983
41984     /**
41985      * Enables the mask so that it can be displayed
41986      */
41987     enable : function(){
41988         this.disabled = false;
41989     },
41990     
41991     onLoadException : function()
41992     {
41993         Roo.log(arguments);
41994         
41995         if (typeof(arguments[3]) != 'undefined') {
41996             Roo.MessageBox.alert("Error loading",arguments[3]);
41997         } 
41998         /*
41999         try {
42000             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42001                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42002             }   
42003         } catch(e) {
42004             
42005         }
42006         */
42007     
42008         
42009         
42010         this.el.unmask(this.removeMask);
42011     },
42012     // private
42013     onLoad : function()
42014     {
42015         this.el.unmask(this.removeMask);
42016     },
42017
42018     // private
42019     onBeforeLoad : function(){
42020         if(!this.disabled){
42021             this.el.mask(this.msg, this.msgCls);
42022         }
42023     },
42024
42025     // private
42026     destroy : function(){
42027         if(this.store){
42028             this.store.un('beforeload', this.onBeforeLoad, this);
42029             this.store.un('load', this.onLoad, this);
42030             this.store.un('loadexception', this.onLoadException, this);
42031         }else{
42032             var um = this.el.getUpdateManager();
42033             um.un('beforeupdate', this.onBeforeLoad, this);
42034             um.un('update', this.onLoad, this);
42035             um.un('failure', this.onLoad, this);
42036         }
42037     }
42038 };/*
42039  * Based on:
42040  * Ext JS Library 1.1.1
42041  * Copyright(c) 2006-2007, Ext JS, LLC.
42042  *
42043  * Originally Released Under LGPL - original licence link has changed is not relivant.
42044  *
42045  * Fork - LGPL
42046  * <script type="text/javascript">
42047  */
42048
42049
42050 /**
42051  * @class Roo.XTemplate
42052  * @extends Roo.Template
42053  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42054 <pre><code>
42055 var t = new Roo.XTemplate(
42056         '&lt;select name="{name}"&gt;',
42057                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42058         '&lt;/select&gt;'
42059 );
42060  
42061 // then append, applying the master template values
42062  </code></pre>
42063  *
42064  * Supported features:
42065  *
42066  *  Tags:
42067
42068 <pre><code>
42069       {a_variable} - output encoded.
42070       {a_variable.format:("Y-m-d")} - call a method on the variable
42071       {a_variable:raw} - unencoded output
42072       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42073       {a_variable:this.method_on_template(...)} - call a method on the template object.
42074  
42075 </code></pre>
42076  *  The tpl tag:
42077 <pre><code>
42078         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42079         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42080         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42081         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42082   
42083         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42084         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42085 </code></pre>
42086  *      
42087  */
42088 Roo.XTemplate = function()
42089 {
42090     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42091     if (this.html) {
42092         this.compile();
42093     }
42094 };
42095
42096
42097 Roo.extend(Roo.XTemplate, Roo.Template, {
42098
42099     /**
42100      * The various sub templates
42101      */
42102     tpls : false,
42103     /**
42104      *
42105      * basic tag replacing syntax
42106      * WORD:WORD()
42107      *
42108      * // you can fake an object call by doing this
42109      *  x.t:(test,tesT) 
42110      * 
42111      */
42112     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42113
42114     /**
42115      * compile the template
42116      *
42117      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42118      *
42119      */
42120     compile: function()
42121     {
42122         var s = this.html;
42123      
42124         s = ['<tpl>', s, '</tpl>'].join('');
42125     
42126         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42127             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42128             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42129             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42130             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42131             m,
42132             id     = 0,
42133             tpls   = [];
42134     
42135         while(true == !!(m = s.match(re))){
42136             var forMatch   = m[0].match(nameRe),
42137                 ifMatch   = m[0].match(ifRe),
42138                 execMatch   = m[0].match(execRe),
42139                 namedMatch   = m[0].match(namedRe),
42140                 
42141                 exp  = null, 
42142                 fn   = null,
42143                 exec = null,
42144                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42145                 
42146             if (ifMatch) {
42147                 // if - puts fn into test..
42148                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42149                 if(exp){
42150                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42151                 }
42152             }
42153             
42154             if (execMatch) {
42155                 // exec - calls a function... returns empty if true is  returned.
42156                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42157                 if(exp){
42158                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42159                 }
42160             }
42161             
42162             
42163             if (name) {
42164                 // for = 
42165                 switch(name){
42166                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42167                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42168                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42169                 }
42170             }
42171             var uid = namedMatch ? namedMatch[1] : id;
42172             
42173             
42174             tpls.push({
42175                 id:     namedMatch ? namedMatch[1] : id,
42176                 target: name,
42177                 exec:   exec,
42178                 test:   fn,
42179                 body:   m[1] || ''
42180             });
42181             if (namedMatch) {
42182                 s = s.replace(m[0], '');
42183             } else { 
42184                 s = s.replace(m[0], '{xtpl'+ id + '}');
42185             }
42186             ++id;
42187         }
42188         this.tpls = [];
42189         for(var i = tpls.length-1; i >= 0; --i){
42190             this.compileTpl(tpls[i]);
42191             this.tpls[tpls[i].id] = tpls[i];
42192         }
42193         this.master = tpls[tpls.length-1];
42194         return this;
42195     },
42196     /**
42197      * same as applyTemplate, except it's done to one of the subTemplates
42198      * when using named templates, you can do:
42199      *
42200      * var str = pl.applySubTemplate('your-name', values);
42201      *
42202      * 
42203      * @param {Number} id of the template
42204      * @param {Object} values to apply to template
42205      * @param {Object} parent (normaly the instance of this object)
42206      */
42207     applySubTemplate : function(id, values, parent)
42208     {
42209         
42210         
42211         var t = this.tpls[id];
42212         
42213         
42214         try { 
42215             if(t.test && !t.test.call(this, values, parent)){
42216                 return '';
42217             }
42218         } catch(e) {
42219             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42220             Roo.log(e.toString());
42221             Roo.log(t.test);
42222             return ''
42223         }
42224         try { 
42225             
42226             if(t.exec && t.exec.call(this, values, parent)){
42227                 return '';
42228             }
42229         } catch(e) {
42230             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42231             Roo.log(e.toString());
42232             Roo.log(t.exec);
42233             return ''
42234         }
42235         try {
42236             var vs = t.target ? t.target.call(this, values, parent) : values;
42237             parent = t.target ? values : parent;
42238             if(t.target && vs instanceof Array){
42239                 var buf = [];
42240                 for(var i = 0, len = vs.length; i < len; i++){
42241                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42242                 }
42243                 return buf.join('');
42244             }
42245             return t.compiled.call(this, vs, parent);
42246         } catch (e) {
42247             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42248             Roo.log(e.toString());
42249             Roo.log(t.compiled);
42250             return '';
42251         }
42252     },
42253
42254     compileTpl : function(tpl)
42255     {
42256         var fm = Roo.util.Format;
42257         var useF = this.disableFormats !== true;
42258         var sep = Roo.isGecko ? "+" : ",";
42259         var undef = function(str) {
42260             Roo.log("Property not found :"  + str);
42261             return '';
42262         };
42263         
42264         var fn = function(m, name, format, args)
42265         {
42266             //Roo.log(arguments);
42267             args = args ? args.replace(/\\'/g,"'") : args;
42268             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42269             if (typeof(format) == 'undefined') {
42270                 format= 'htmlEncode';
42271             }
42272             if (format == 'raw' ) {
42273                 format = false;
42274             }
42275             
42276             if(name.substr(0, 4) == 'xtpl'){
42277                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42278             }
42279             
42280             // build an array of options to determine if value is undefined..
42281             
42282             // basically get 'xxxx.yyyy' then do
42283             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42284             //    (function () { Roo.log("Property not found"); return ''; })() :
42285             //    ......
42286             
42287             var udef_ar = [];
42288             var lookfor = '';
42289             Roo.each(name.split('.'), function(st) {
42290                 lookfor += (lookfor.length ? '.': '') + st;
42291                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42292             });
42293             
42294             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42295             
42296             
42297             if(format && useF){
42298                 
42299                 args = args ? ',' + args : "";
42300                  
42301                 if(format.substr(0, 5) != "this."){
42302                     format = "fm." + format + '(';
42303                 }else{
42304                     format = 'this.call("'+ format.substr(5) + '", ';
42305                     args = ", values";
42306                 }
42307                 
42308                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42309             }
42310              
42311             if (args.length) {
42312                 // called with xxyx.yuu:(test,test)
42313                 // change to ()
42314                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42315             }
42316             // raw.. - :raw modifier..
42317             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42318             
42319         };
42320         var body;
42321         // branched to use + in gecko and [].join() in others
42322         if(Roo.isGecko){
42323             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42324                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42325                     "';};};";
42326         }else{
42327             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42328             body.push(tpl.body.replace(/(\r\n|\n)/g,
42329                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42330             body.push("'].join('');};};");
42331             body = body.join('');
42332         }
42333         
42334         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42335        
42336         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42337         eval(body);
42338         
42339         return this;
42340     },
42341
42342     applyTemplate : function(values){
42343         return this.master.compiled.call(this, values, {});
42344         //var s = this.subs;
42345     },
42346
42347     apply : function(){
42348         return this.applyTemplate.apply(this, arguments);
42349     }
42350
42351  });
42352
42353 Roo.XTemplate.from = function(el){
42354     el = Roo.getDom(el);
42355     return new Roo.XTemplate(el.value || el.innerHTML);
42356 };