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                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @constructor
6172  * Create a new JsonReader
6173  * @param {Object} meta Metadata configuration options
6174  * @param {Object} recordType Either an Array of field definition objects,
6175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6176  */
6177 Roo.data.JsonReader = function(meta, recordType){
6178     
6179     meta = meta || {};
6180     // set some defaults:
6181     Roo.applyIf(meta, {
6182         totalProperty: 'total',
6183         successProperty : 'success',
6184         root : 'data',
6185         id : 'id'
6186     });
6187     
6188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6189 };
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6191     
6192     /**
6193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6194      * Used by Store query builder to append _requestMeta to params.
6195      * 
6196      */
6197     metaFromRemote : false,
6198     /**
6199      * This method is only used by a DataProxy which has retrieved data from a remote server.
6200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     read : function(response){
6205         var json = response.responseText;
6206        
6207         var o = /* eval:var:o */ eval("("+json+")");
6208         if(!o) {
6209             throw {message: "JsonReader.read: Json object not found"};
6210         }
6211         
6212         if(o.metaData){
6213             
6214             delete this.ef;
6215             this.metaFromRemote = true;
6216             this.meta = o.metaData;
6217             this.recordType = Roo.data.Record.create(o.metaData.fields);
6218             this.onMetaChange(this.meta, this.recordType, o);
6219         }
6220         return this.readRecords(o);
6221     },
6222
6223     // private function a store will implement
6224     onMetaChange : function(meta, recordType, o){
6225
6226     },
6227
6228     /**
6229          * @ignore
6230          */
6231     simpleAccess: function(obj, subsc) {
6232         return obj[subsc];
6233     },
6234
6235         /**
6236          * @ignore
6237          */
6238     getJsonAccessor: function(){
6239         var re = /[\[\.]/;
6240         return function(expr) {
6241             try {
6242                 return(re.test(expr))
6243                     ? new Function("obj", "return obj." + expr)
6244                     : function(obj){
6245                         return obj[expr];
6246                     };
6247             } catch(e){}
6248             return Roo.emptyFn;
6249         };
6250     }(),
6251
6252     /**
6253      * Create a data block containing Roo.data.Records from an XML document.
6254      * @param {Object} o An object which contains an Array of row objects in the property specified
6255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256      * which contains the total size of the dataset.
6257      * @return {Object} data A data block which is used by an Roo.data.Store object as
6258      * a cache of Roo.data.Records.
6259      */
6260     readRecords : function(o){
6261         /**
6262          * After any data loads, the raw JSON data is available for further custom processing.
6263          * @type Object
6264          */
6265         this.o = o;
6266         var s = this.meta, Record = this.recordType,
6267             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6268
6269 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6270         if (!this.ef) {
6271             if(s.totalProperty) {
6272                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6273                 }
6274                 if(s.successProperty) {
6275                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6276                 }
6277                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6278                 if (s.id) {
6279                         var g = this.getJsonAccessor(s.id);
6280                         this.getId = function(rec) {
6281                                 var r = g(rec);  
6282                                 return (r === undefined || r === "") ? null : r;
6283                         };
6284                 } else {
6285                         this.getId = function(){return null;};
6286                 }
6287             this.ef = [];
6288             for(var jj = 0; jj < fl; jj++){
6289                 f = fi[jj];
6290                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6291                 this.ef[jj] = this.getJsonAccessor(map);
6292             }
6293         }
6294
6295         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6296         if(s.totalProperty){
6297             var vt = parseInt(this.getTotal(o), 10);
6298             if(!isNaN(vt)){
6299                 totalRecords = vt;
6300             }
6301         }
6302         if(s.successProperty){
6303             var vs = this.getSuccess(o);
6304             if(vs === false || vs === 'false'){
6305                 success = false;
6306             }
6307         }
6308         var records = [];
6309         for(var i = 0; i < c; i++){
6310                 var n = root[i];
6311             var values = {};
6312             var id = this.getId(n);
6313             for(var j = 0; j < fl; j++){
6314                 f = fi[j];
6315             var v = this.ef[j](n);
6316             if (!f.convert) {
6317                 Roo.log('missing convert for ' + f.name);
6318                 Roo.log(f);
6319                 continue;
6320             }
6321             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6322             }
6323             var record = new Record(values, id);
6324             record.json = n;
6325             records[i] = record;
6326         }
6327         return {
6328             raw : o,
6329             success : success,
6330             records : records,
6331             totalRecords : totalRecords
6332         };
6333     }
6334 });/*
6335  * Based on:
6336  * Ext JS Library 1.1.1
6337  * Copyright(c) 2006-2007, Ext JS, LLC.
6338  *
6339  * Originally Released Under LGPL - original licence link has changed is not relivant.
6340  *
6341  * Fork - LGPL
6342  * <script type="text/javascript">
6343  */
6344
6345 /**
6346  * @class Roo.data.XmlReader
6347  * @extends Roo.data.DataReader
6348  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6349  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6350  * <p>
6351  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6352  * header in the HTTP response must be set to "text/xml".</em>
6353  * <p>
6354  * Example code:
6355  * <pre><code>
6356 var RecordDef = Roo.data.Record.create([
6357    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6358    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6359 ]);
6360 var myReader = new Roo.data.XmlReader({
6361    totalRecords: "results", // The element which contains the total dataset size (optional)
6362    record: "row",           // The repeated element which contains row information
6363    id: "id"                 // The element within the row that provides an ID for the record (optional)
6364 }, RecordDef);
6365 </code></pre>
6366  * <p>
6367  * This would consume an XML file like this:
6368  * <pre><code>
6369 &lt;?xml?>
6370 &lt;dataset>
6371  &lt;results>2&lt;/results>
6372  &lt;row>
6373    &lt;id>1&lt;/id>
6374    &lt;name>Bill&lt;/name>
6375    &lt;occupation>Gardener&lt;/occupation>
6376  &lt;/row>
6377  &lt;row>
6378    &lt;id>2&lt;/id>
6379    &lt;name>Ben&lt;/name>
6380    &lt;occupation>Horticulturalist&lt;/occupation>
6381  &lt;/row>
6382 &lt;/dataset>
6383 </code></pre>
6384  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6385  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6386  * paged from the remote server.
6387  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6388  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6389  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6390  * a record identifier value.
6391  * @constructor
6392  * Create a new XmlReader
6393  * @param {Object} meta Metadata configuration options
6394  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6395  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6396  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6397  */
6398 Roo.data.XmlReader = function(meta, recordType){
6399     meta = meta || {};
6400     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6401 };
6402 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6403     /**
6404      * This method is only used by a DataProxy which has retrieved data from a remote server.
6405          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6406          * to contain a method called 'responseXML' that returns an XML document object.
6407      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6408      * a cache of Roo.data.Records.
6409      */
6410     read : function(response){
6411         var doc = response.responseXML;
6412         if(!doc) {
6413             throw {message: "XmlReader.read: XML Document not available"};
6414         }
6415         return this.readRecords(doc);
6416     },
6417
6418     /**
6419      * Create a data block containing Roo.data.Records from an XML document.
6420          * @param {Object} doc A parsed XML document.
6421      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6422      * a cache of Roo.data.Records.
6423      */
6424     readRecords : function(doc){
6425         /**
6426          * After any data loads/reads, the raw XML Document is available for further custom processing.
6427          * @type XMLDocument
6428          */
6429         this.xmlData = doc;
6430         var root = doc.documentElement || doc;
6431         var q = Roo.DomQuery;
6432         var recordType = this.recordType, fields = recordType.prototype.fields;
6433         var sid = this.meta.id;
6434         var totalRecords = 0, success = true;
6435         if(this.meta.totalRecords){
6436             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6437         }
6438         
6439         if(this.meta.success){
6440             var sv = q.selectValue(this.meta.success, root, true);
6441             success = sv !== false && sv !== 'false';
6442         }
6443         var records = [];
6444         var ns = q.select(this.meta.record, root);
6445         for(var i = 0, len = ns.length; i < len; i++) {
6446                 var n = ns[i];
6447                 var values = {};
6448                 var id = sid ? q.selectValue(sid, n) : undefined;
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                     var f = fields.items[j];
6451                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6452                     v = f.convert(v);
6453                     values[f.name] = v;
6454                 }
6455                 var record = new recordType(values, id);
6456                 record.node = n;
6457                 records[records.length] = record;
6458             }
6459
6460             return {
6461                 success : success,
6462                 records : records,
6463                 totalRecords : totalRecords || records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477 /**
6478  * @class Roo.data.ArrayReader
6479  * @extends Roo.data.DataReader
6480  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6481  * Each element of that Array represents a row of data fields. The
6482  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6483  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6484  * <p>
6485  * Example code:.
6486  * <pre><code>
6487 var RecordDef = Roo.data.Record.create([
6488     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6489     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6490 ]);
6491 var myReader = new Roo.data.ArrayReader({
6492     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6493 }, RecordDef);
6494 </code></pre>
6495  * <p>
6496  * This would consume an Array like this:
6497  * <pre><code>
6498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6499   </code></pre>
6500  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6501  * @constructor
6502  * Create a new JsonReader
6503  * @param {Object} meta Metadata configuration options.
6504  * @param {Object} recordType Either an Array of field definition objects
6505  * as specified to {@link Roo.data.Record#create},
6506  * or an {@link Roo.data.Record} object
6507  * created using {@link Roo.data.Record#create}.
6508  */
6509 Roo.data.ArrayReader = function(meta, recordType){
6510     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6511 };
6512
6513 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6514     /**
6515      * Create a data block containing Roo.data.Records from an XML document.
6516      * @param {Object} o An Array of row objects which represents the dataset.
6517      * @return {Object} data A data block which is used by an Roo.data.Store object as
6518      * a cache of Roo.data.Records.
6519      */
6520     readRecords : function(o){
6521         var sid = this.meta ? this.meta.id : null;
6522         var recordType = this.recordType, fields = recordType.prototype.fields;
6523         var records = [];
6524         var root = o;
6525             for(var i = 0; i < root.length; i++){
6526                     var n = root[i];
6527                 var values = {};
6528                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6529                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6530                 var f = fields.items[j];
6531                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6532                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6533                 v = f.convert(v);
6534                 values[f.name] = v;
6535             }
6536                 var record = new recordType(values, id);
6537                 record.json = n;
6538                 records[records.length] = record;
6539             }
6540             return {
6541                 records : records,
6542                 totalRecords : records.length
6543             };
6544     }
6545 });/*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555
6556
6557 /**
6558  * @class Roo.data.Tree
6559  * @extends Roo.util.Observable
6560  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6561  * in the tree have most standard DOM functionality.
6562  * @constructor
6563  * @param {Node} root (optional) The root node
6564  */
6565 Roo.data.Tree = function(root){
6566    this.nodeHash = {};
6567    /**
6568     * The root node for this tree
6569     * @type Node
6570     */
6571    this.root = null;
6572    if(root){
6573        this.setRootNode(root);
6574    }
6575    this.addEvents({
6576        /**
6577         * @event append
6578         * Fires when a new child node is appended to a node in this tree.
6579         * @param {Tree} tree The owner tree
6580         * @param {Node} parent The parent node
6581         * @param {Node} node The newly appended node
6582         * @param {Number} index The index of the newly appended node
6583         */
6584        "append" : true,
6585        /**
6586         * @event remove
6587         * Fires when a child node is removed from a node in this tree.
6588         * @param {Tree} tree The owner tree
6589         * @param {Node} parent The parent node
6590         * @param {Node} node The child node removed
6591         */
6592        "remove" : true,
6593        /**
6594         * @event move
6595         * Fires when a node is moved to a new location in the tree
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} node The node moved
6598         * @param {Node} oldParent The old parent of this node
6599         * @param {Node} newParent The new parent of this node
6600         * @param {Number} index The index it was moved to
6601         */
6602        "move" : true,
6603        /**
6604         * @event insert
6605         * Fires when a new child node is inserted in a node in this tree.
6606         * @param {Tree} tree The owner tree
6607         * @param {Node} parent The parent node
6608         * @param {Node} node The child node inserted
6609         * @param {Node} refNode The child node the node was inserted before
6610         */
6611        "insert" : true,
6612        /**
6613         * @event beforeappend
6614         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} parent The parent node
6617         * @param {Node} node The child node to be appended
6618         */
6619        "beforeappend" : true,
6620        /**
6621         * @event beforeremove
6622         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6623         * @param {Tree} tree The owner tree
6624         * @param {Node} parent The parent node
6625         * @param {Node} node The child node to be removed
6626         */
6627        "beforeremove" : true,
6628        /**
6629         * @event beforemove
6630         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6631         * @param {Tree} tree The owner tree
6632         * @param {Node} node The node being moved
6633         * @param {Node} oldParent The parent of the node
6634         * @param {Node} newParent The new parent the node is moving to
6635         * @param {Number} index The index it is being moved to
6636         */
6637        "beforemove" : true,
6638        /**
6639         * @event beforeinsert
6640         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6641         * @param {Tree} tree The owner tree
6642         * @param {Node} parent The parent node
6643         * @param {Node} node The child node to be inserted
6644         * @param {Node} refNode The child node the node is being inserted before
6645         */
6646        "beforeinsert" : true
6647    });
6648
6649     Roo.data.Tree.superclass.constructor.call(this);
6650 };
6651
6652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6653     pathSeparator: "/",
6654
6655     proxyNodeEvent : function(){
6656         return this.fireEvent.apply(this, arguments);
6657     },
6658
6659     /**
6660      * Returns the root node for this tree.
6661      * @return {Node}
6662      */
6663     getRootNode : function(){
6664         return this.root;
6665     },
6666
6667     /**
6668      * Sets the root node for this tree.
6669      * @param {Node} node
6670      * @return {Node}
6671      */
6672     setRootNode : function(node){
6673         this.root = node;
6674         node.ownerTree = this;
6675         node.isRoot = true;
6676         this.registerNode(node);
6677         return node;
6678     },
6679
6680     /**
6681      * Gets a node in this tree by its id.
6682      * @param {String} id
6683      * @return {Node}
6684      */
6685     getNodeById : function(id){
6686         return this.nodeHash[id];
6687     },
6688
6689     registerNode : function(node){
6690         this.nodeHash[node.id] = node;
6691     },
6692
6693     unregisterNode : function(node){
6694         delete this.nodeHash[node.id];
6695     },
6696
6697     toString : function(){
6698         return "[Tree"+(this.id?" "+this.id:"")+"]";
6699     }
6700 });
6701
6702 /**
6703  * @class Roo.data.Node
6704  * @extends Roo.util.Observable
6705  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6706  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6707  * @constructor
6708  * @param {Object} attributes The attributes/config for the node
6709  */
6710 Roo.data.Node = function(attributes){
6711     /**
6712      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6713      * @type {Object}
6714      */
6715     this.attributes = attributes || {};
6716     this.leaf = this.attributes.leaf;
6717     /**
6718      * The node id. @type String
6719      */
6720     this.id = this.attributes.id;
6721     if(!this.id){
6722         this.id = Roo.id(null, "ynode-");
6723         this.attributes.id = this.id;
6724     }
6725      
6726     
6727     /**
6728      * All child nodes of this node. @type Array
6729      */
6730     this.childNodes = [];
6731     if(!this.childNodes.indexOf){ // indexOf is a must
6732         this.childNodes.indexOf = function(o){
6733             for(var i = 0, len = this.length; i < len; i++){
6734                 if(this[i] == o) {
6735                     return i;
6736                 }
6737             }
6738             return -1;
6739         };
6740     }
6741     /**
6742      * The parent node for this node. @type Node
6743      */
6744     this.parentNode = null;
6745     /**
6746      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6747      */
6748     this.firstChild = null;
6749     /**
6750      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6751      */
6752     this.lastChild = null;
6753     /**
6754      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6755      */
6756     this.previousSibling = null;
6757     /**
6758      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6759      */
6760     this.nextSibling = null;
6761
6762     this.addEvents({
6763        /**
6764         * @event append
6765         * Fires when a new child node is appended
6766         * @param {Tree} tree The owner tree
6767         * @param {Node} this This node
6768         * @param {Node} node The newly appended node
6769         * @param {Number} index The index of the newly appended node
6770         */
6771        "append" : true,
6772        /**
6773         * @event remove
6774         * Fires when a child node is removed
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The removed node
6778         */
6779        "remove" : true,
6780        /**
6781         * @event move
6782         * Fires when this node is moved to a new location in the tree
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} oldParent The old parent of this node
6786         * @param {Node} newParent The new parent of this node
6787         * @param {Number} index The index it was moved to
6788         */
6789        "move" : true,
6790        /**
6791         * @event insert
6792         * Fires when a new child node is inserted.
6793         * @param {Tree} tree The owner tree
6794         * @param {Node} this This node
6795         * @param {Node} node The child node inserted
6796         * @param {Node} refNode The child node the node was inserted before
6797         */
6798        "insert" : true,
6799        /**
6800         * @event beforeappend
6801         * Fires before a new child is appended, return false to cancel the append.
6802         * @param {Tree} tree The owner tree
6803         * @param {Node} this This node
6804         * @param {Node} node The child node to be appended
6805         */
6806        "beforeappend" : true,
6807        /**
6808         * @event beforeremove
6809         * Fires before a child is removed, return false to cancel the remove.
6810         * @param {Tree} tree The owner tree
6811         * @param {Node} this This node
6812         * @param {Node} node The child node to be removed
6813         */
6814        "beforeremove" : true,
6815        /**
6816         * @event beforemove
6817         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6818         * @param {Tree} tree The owner tree
6819         * @param {Node} this This node
6820         * @param {Node} oldParent The parent of this node
6821         * @param {Node} newParent The new parent this node is moving to
6822         * @param {Number} index The index it is being moved to
6823         */
6824        "beforemove" : true,
6825        /**
6826         * @event beforeinsert
6827         * Fires before a new child is inserted, return false to cancel the insert.
6828         * @param {Tree} tree The owner tree
6829         * @param {Node} this This node
6830         * @param {Node} node The child node to be inserted
6831         * @param {Node} refNode The child node the node is being inserted before
6832         */
6833        "beforeinsert" : true
6834    });
6835     this.listeners = this.attributes.listeners;
6836     Roo.data.Node.superclass.constructor.call(this);
6837 };
6838
6839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6840     fireEvent : function(evtName){
6841         // first do standard event for this node
6842         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6843             return false;
6844         }
6845         // then bubble it up to the tree if the event wasn't cancelled
6846         var ot = this.getOwnerTree();
6847         if(ot){
6848             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6849                 return false;
6850             }
6851         }
6852         return true;
6853     },
6854
6855     /**
6856      * Returns true if this node is a leaf
6857      * @return {Boolean}
6858      */
6859     isLeaf : function(){
6860         return this.leaf === true;
6861     },
6862
6863     // private
6864     setFirstChild : function(node){
6865         this.firstChild = node;
6866     },
6867
6868     //private
6869     setLastChild : function(node){
6870         this.lastChild = node;
6871     },
6872
6873
6874     /**
6875      * Returns true if this node is the last child of its parent
6876      * @return {Boolean}
6877      */
6878     isLast : function(){
6879        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6880     },
6881
6882     /**
6883      * Returns true if this node is the first child of its parent
6884      * @return {Boolean}
6885      */
6886     isFirst : function(){
6887        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6888     },
6889
6890     hasChildNodes : function(){
6891         return !this.isLeaf() && this.childNodes.length > 0;
6892     },
6893
6894     /**
6895      * Insert node(s) as the last child node of this node.
6896      * @param {Node/Array} node The node or Array of nodes to append
6897      * @return {Node} The appended node if single append, or null if an array was passed
6898      */
6899     appendChild : function(node){
6900         var multi = false;
6901         if(node instanceof Array){
6902             multi = node;
6903         }else if(arguments.length > 1){
6904             multi = arguments;
6905         }
6906         // if passed an array or multiple args do them one by one
6907         if(multi){
6908             for(var i = 0, len = multi.length; i < len; i++) {
6909                 this.appendChild(multi[i]);
6910             }
6911         }else{
6912             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6913                 return false;
6914             }
6915             var index = this.childNodes.length;
6916             var oldParent = node.parentNode;
6917             // it's a move, make sure we move it cleanly
6918             if(oldParent){
6919                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6920                     return false;
6921                 }
6922                 oldParent.removeChild(node);
6923             }
6924             index = this.childNodes.length;
6925             if(index == 0){
6926                 this.setFirstChild(node);
6927             }
6928             this.childNodes.push(node);
6929             node.parentNode = this;
6930             var ps = this.childNodes[index-1];
6931             if(ps){
6932                 node.previousSibling = ps;
6933                 ps.nextSibling = node;
6934             }else{
6935                 node.previousSibling = null;
6936             }
6937             node.nextSibling = null;
6938             this.setLastChild(node);
6939             node.setOwnerTree(this.getOwnerTree());
6940             this.fireEvent("append", this.ownerTree, this, node, index);
6941             if(oldParent){
6942                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6943             }
6944             return node;
6945         }
6946     },
6947
6948     /**
6949      * Removes a child node from this node.
6950      * @param {Node} node The node to remove
6951      * @return {Node} The removed node
6952      */
6953     removeChild : function(node){
6954         var index = this.childNodes.indexOf(node);
6955         if(index == -1){
6956             return false;
6957         }
6958         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6959             return false;
6960         }
6961
6962         // remove it from childNodes collection
6963         this.childNodes.splice(index, 1);
6964
6965         // update siblings
6966         if(node.previousSibling){
6967             node.previousSibling.nextSibling = node.nextSibling;
6968         }
6969         if(node.nextSibling){
6970             node.nextSibling.previousSibling = node.previousSibling;
6971         }
6972
6973         // update child refs
6974         if(this.firstChild == node){
6975             this.setFirstChild(node.nextSibling);
6976         }
6977         if(this.lastChild == node){
6978             this.setLastChild(node.previousSibling);
6979         }
6980
6981         node.setOwnerTree(null);
6982         // clear any references from the node
6983         node.parentNode = null;
6984         node.previousSibling = null;
6985         node.nextSibling = null;
6986         this.fireEvent("remove", this.ownerTree, this, node);
6987         return node;
6988     },
6989
6990     /**
6991      * Inserts the first node before the second node in this nodes childNodes collection.
6992      * @param {Node} node The node to insert
6993      * @param {Node} refNode The node to insert before (if null the node is appended)
6994      * @return {Node} The inserted node
6995      */
6996     insertBefore : function(node, refNode){
6997         if(!refNode){ // like standard Dom, refNode can be null for append
6998             return this.appendChild(node);
6999         }
7000         // nothing to do
7001         if(node == refNode){
7002             return false;
7003         }
7004
7005         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7006             return false;
7007         }
7008         var index = this.childNodes.indexOf(refNode);
7009         var oldParent = node.parentNode;
7010         var refIndex = index;
7011
7012         // when moving internally, indexes will change after remove
7013         if(oldParent == this && this.childNodes.indexOf(node) < index){
7014             refIndex--;
7015         }
7016
7017         // it's a move, make sure we move it cleanly
7018         if(oldParent){
7019             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7020                 return false;
7021             }
7022             oldParent.removeChild(node);
7023         }
7024         if(refIndex == 0){
7025             this.setFirstChild(node);
7026         }
7027         this.childNodes.splice(refIndex, 0, node);
7028         node.parentNode = this;
7029         var ps = this.childNodes[refIndex-1];
7030         if(ps){
7031             node.previousSibling = ps;
7032             ps.nextSibling = node;
7033         }else{
7034             node.previousSibling = null;
7035         }
7036         node.nextSibling = refNode;
7037         refNode.previousSibling = node;
7038         node.setOwnerTree(this.getOwnerTree());
7039         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7040         if(oldParent){
7041             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7042         }
7043         return node;
7044     },
7045
7046     /**
7047      * Returns the child node at the specified index.
7048      * @param {Number} index
7049      * @return {Node}
7050      */
7051     item : function(index){
7052         return this.childNodes[index];
7053     },
7054
7055     /**
7056      * Replaces one child node in this node with another.
7057      * @param {Node} newChild The replacement node
7058      * @param {Node} oldChild The node to replace
7059      * @return {Node} The replaced node
7060      */
7061     replaceChild : function(newChild, oldChild){
7062         this.insertBefore(newChild, oldChild);
7063         this.removeChild(oldChild);
7064         return oldChild;
7065     },
7066
7067     /**
7068      * Returns the index of a child node
7069      * @param {Node} node
7070      * @return {Number} The index of the node or -1 if it was not found
7071      */
7072     indexOf : function(child){
7073         return this.childNodes.indexOf(child);
7074     },
7075
7076     /**
7077      * Returns the tree this node is in.
7078      * @return {Tree}
7079      */
7080     getOwnerTree : function(){
7081         // if it doesn't have one, look for one
7082         if(!this.ownerTree){
7083             var p = this;
7084             while(p){
7085                 if(p.ownerTree){
7086                     this.ownerTree = p.ownerTree;
7087                     break;
7088                 }
7089                 p = p.parentNode;
7090             }
7091         }
7092         return this.ownerTree;
7093     },
7094
7095     /**
7096      * Returns depth of this node (the root node has a depth of 0)
7097      * @return {Number}
7098      */
7099     getDepth : function(){
7100         var depth = 0;
7101         var p = this;
7102         while(p.parentNode){
7103             ++depth;
7104             p = p.parentNode;
7105         }
7106         return depth;
7107     },
7108
7109     // private
7110     setOwnerTree : function(tree){
7111         // if it's move, we need to update everyone
7112         if(tree != this.ownerTree){
7113             if(this.ownerTree){
7114                 this.ownerTree.unregisterNode(this);
7115             }
7116             this.ownerTree = tree;
7117             var cs = this.childNodes;
7118             for(var i = 0, len = cs.length; i < len; i++) {
7119                 cs[i].setOwnerTree(tree);
7120             }
7121             if(tree){
7122                 tree.registerNode(this);
7123             }
7124         }
7125     },
7126
7127     /**
7128      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7129      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7130      * @return {String} The path
7131      */
7132     getPath : function(attr){
7133         attr = attr || "id";
7134         var p = this.parentNode;
7135         var b = [this.attributes[attr]];
7136         while(p){
7137             b.unshift(p.attributes[attr]);
7138             p = p.parentNode;
7139         }
7140         var sep = this.getOwnerTree().pathSeparator;
7141         return sep + b.join(sep);
7142     },
7143
7144     /**
7145      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7146      * function call will be the scope provided or the current node. The arguments to the function
7147      * will be the args provided or the current node. If the function returns false at any point,
7148      * the bubble is stopped.
7149      * @param {Function} fn The function to call
7150      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7151      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7152      */
7153     bubble : function(fn, scope, args){
7154         var p = this;
7155         while(p){
7156             if(fn.call(scope || p, args || p) === false){
7157                 break;
7158             }
7159             p = p.parentNode;
7160         }
7161     },
7162
7163     /**
7164      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7165      * function call will be the scope provided or the current node. The arguments to the function
7166      * will be the args provided or the current node. If the function returns false at any point,
7167      * the cascade is stopped on that branch.
7168      * @param {Function} fn The function to call
7169      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7170      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7171      */
7172     cascade : function(fn, scope, args){
7173         if(fn.call(scope || this, args || this) !== false){
7174             var cs = this.childNodes;
7175             for(var i = 0, len = cs.length; i < len; i++) {
7176                 cs[i].cascade(fn, scope, args);
7177             }
7178         }
7179     },
7180
7181     /**
7182      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7183      * function call will be the scope provided or the current node. The arguments to the function
7184      * will be the args provided or the current node. If the function returns false at any point,
7185      * the iteration stops.
7186      * @param {Function} fn The function to call
7187      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7188      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7189      */
7190     eachChild : function(fn, scope, args){
7191         var cs = this.childNodes;
7192         for(var i = 0, len = cs.length; i < len; i++) {
7193                 if(fn.call(scope || this, args || cs[i]) === false){
7194                     break;
7195                 }
7196         }
7197     },
7198
7199     /**
7200      * Finds the first child that has the attribute with the specified value.
7201      * @param {String} attribute The attribute name
7202      * @param {Mixed} value The value to search for
7203      * @return {Node} The found child or null if none was found
7204      */
7205     findChild : function(attribute, value){
7206         var cs = this.childNodes;
7207         for(var i = 0, len = cs.length; i < len; i++) {
7208                 if(cs[i].attributes[attribute] == value){
7209                     return cs[i];
7210                 }
7211         }
7212         return null;
7213     },
7214
7215     /**
7216      * Finds the first child by a custom function. The child matches if the function passed
7217      * returns true.
7218      * @param {Function} fn
7219      * @param {Object} scope (optional)
7220      * @return {Node} The found child or null if none was found
7221      */
7222     findChildBy : function(fn, scope){
7223         var cs = this.childNodes;
7224         for(var i = 0, len = cs.length; i < len; i++) {
7225                 if(fn.call(scope||cs[i], cs[i]) === true){
7226                     return cs[i];
7227                 }
7228         }
7229         return null;
7230     },
7231
7232     /**
7233      * Sorts this nodes children using the supplied sort function
7234      * @param {Function} fn
7235      * @param {Object} scope (optional)
7236      */
7237     sort : function(fn, scope){
7238         var cs = this.childNodes;
7239         var len = cs.length;
7240         if(len > 0){
7241             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7242             cs.sort(sortFn);
7243             for(var i = 0; i < len; i++){
7244                 var n = cs[i];
7245                 n.previousSibling = cs[i-1];
7246                 n.nextSibling = cs[i+1];
7247                 if(i == 0){
7248                     this.setFirstChild(n);
7249                 }
7250                 if(i == len-1){
7251                     this.setLastChild(n);
7252                 }
7253             }
7254         }
7255     },
7256
7257     /**
7258      * Returns true if this node is an ancestor (at any point) of the passed node.
7259      * @param {Node} node
7260      * @return {Boolean}
7261      */
7262     contains : function(node){
7263         return node.isAncestor(this);
7264     },
7265
7266     /**
7267      * Returns true if the passed node is an ancestor (at any point) of this node.
7268      * @param {Node} node
7269      * @return {Boolean}
7270      */
7271     isAncestor : function(node){
7272         var p = this.parentNode;
7273         while(p){
7274             if(p == node){
7275                 return true;
7276             }
7277             p = p.parentNode;
7278         }
7279         return false;
7280     },
7281
7282     toString : function(){
7283         return "[Node"+(this.id?" "+this.id:"")+"]";
7284     }
7285 });/*
7286  * Based on:
7287  * Ext JS Library 1.1.1
7288  * Copyright(c) 2006-2007, Ext JS, LLC.
7289  *
7290  * Originally Released Under LGPL - original licence link has changed is not relivant.
7291  *
7292  * Fork - LGPL
7293  * <script type="text/javascript">
7294  */
7295  (function(){ 
7296 /**
7297  * @class Roo.Layer
7298  * @extends Roo.Element
7299  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7300  * automatic maintaining of shadow/shim positions.
7301  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7302  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7303  * you can pass a string with a CSS class name. False turns off the shadow.
7304  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7305  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7306  * @cfg {String} cls CSS class to add to the element
7307  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7308  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7309  * @constructor
7310  * @param {Object} config An object with config options.
7311  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7312  */
7313
7314 Roo.Layer = function(config, existingEl){
7315     config = config || {};
7316     var dh = Roo.DomHelper;
7317     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7318     if(existingEl){
7319         this.dom = Roo.getDom(existingEl);
7320     }
7321     if(!this.dom){
7322         var o = config.dh || {tag: "div", cls: "x-layer"};
7323         this.dom = dh.append(pel, o);
7324     }
7325     if(config.cls){
7326         this.addClass(config.cls);
7327     }
7328     this.constrain = config.constrain !== false;
7329     this.visibilityMode = Roo.Element.VISIBILITY;
7330     if(config.id){
7331         this.id = this.dom.id = config.id;
7332     }else{
7333         this.id = Roo.id(this.dom);
7334     }
7335     this.zindex = config.zindex || this.getZIndex();
7336     this.position("absolute", this.zindex);
7337     if(config.shadow){
7338         this.shadowOffset = config.shadowOffset || 4;
7339         this.shadow = new Roo.Shadow({
7340             offset : this.shadowOffset,
7341             mode : config.shadow
7342         });
7343     }else{
7344         this.shadowOffset = 0;
7345     }
7346     this.useShim = config.shim !== false && Roo.useShims;
7347     this.useDisplay = config.useDisplay;
7348     this.hide();
7349 };
7350
7351 var supr = Roo.Element.prototype;
7352
7353 // shims are shared among layer to keep from having 100 iframes
7354 var shims = [];
7355
7356 Roo.extend(Roo.Layer, Roo.Element, {
7357
7358     getZIndex : function(){
7359         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7360     },
7361
7362     getShim : function(){
7363         if(!this.useShim){
7364             return null;
7365         }
7366         if(this.shim){
7367             return this.shim;
7368         }
7369         var shim = shims.shift();
7370         if(!shim){
7371             shim = this.createShim();
7372             shim.enableDisplayMode('block');
7373             shim.dom.style.display = 'none';
7374             shim.dom.style.visibility = 'visible';
7375         }
7376         var pn = this.dom.parentNode;
7377         if(shim.dom.parentNode != pn){
7378             pn.insertBefore(shim.dom, this.dom);
7379         }
7380         shim.setStyle('z-index', this.getZIndex()-2);
7381         this.shim = shim;
7382         return shim;
7383     },
7384
7385     hideShim : function(){
7386         if(this.shim){
7387             this.shim.setDisplayed(false);
7388             shims.push(this.shim);
7389             delete this.shim;
7390         }
7391     },
7392
7393     disableShadow : function(){
7394         if(this.shadow){
7395             this.shadowDisabled = true;
7396             this.shadow.hide();
7397             this.lastShadowOffset = this.shadowOffset;
7398             this.shadowOffset = 0;
7399         }
7400     },
7401
7402     enableShadow : function(show){
7403         if(this.shadow){
7404             this.shadowDisabled = false;
7405             this.shadowOffset = this.lastShadowOffset;
7406             delete this.lastShadowOffset;
7407             if(show){
7408                 this.sync(true);
7409             }
7410         }
7411     },
7412
7413     // private
7414     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7415     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7416     sync : function(doShow){
7417         var sw = this.shadow;
7418         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7419             var sh = this.getShim();
7420
7421             var w = this.getWidth(),
7422                 h = this.getHeight();
7423
7424             var l = this.getLeft(true),
7425                 t = this.getTop(true);
7426
7427             if(sw && !this.shadowDisabled){
7428                 if(doShow && !sw.isVisible()){
7429                     sw.show(this);
7430                 }else{
7431                     sw.realign(l, t, w, h);
7432                 }
7433                 if(sh){
7434                     if(doShow){
7435                        sh.show();
7436                     }
7437                     // fit the shim behind the shadow, so it is shimmed too
7438                     var a = sw.adjusts, s = sh.dom.style;
7439                     s.left = (Math.min(l, l+a.l))+"px";
7440                     s.top = (Math.min(t, t+a.t))+"px";
7441                     s.width = (w+a.w)+"px";
7442                     s.height = (h+a.h)+"px";
7443                 }
7444             }else if(sh){
7445                 if(doShow){
7446                    sh.show();
7447                 }
7448                 sh.setSize(w, h);
7449                 sh.setLeftTop(l, t);
7450             }
7451             
7452         }
7453     },
7454
7455     // private
7456     destroy : function(){
7457         this.hideShim();
7458         if(this.shadow){
7459             this.shadow.hide();
7460         }
7461         this.removeAllListeners();
7462         var pn = this.dom.parentNode;
7463         if(pn){
7464             pn.removeChild(this.dom);
7465         }
7466         Roo.Element.uncache(this.id);
7467     },
7468
7469     remove : function(){
7470         this.destroy();
7471     },
7472
7473     // private
7474     beginUpdate : function(){
7475         this.updating = true;
7476     },
7477
7478     // private
7479     endUpdate : function(){
7480         this.updating = false;
7481         this.sync(true);
7482     },
7483
7484     // private
7485     hideUnders : function(negOffset){
7486         if(this.shadow){
7487             this.shadow.hide();
7488         }
7489         this.hideShim();
7490     },
7491
7492     // private
7493     constrainXY : function(){
7494         if(this.constrain){
7495             var vw = Roo.lib.Dom.getViewWidth(),
7496                 vh = Roo.lib.Dom.getViewHeight();
7497             var s = Roo.get(document).getScroll();
7498
7499             var xy = this.getXY();
7500             var x = xy[0], y = xy[1];   
7501             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7502             // only move it if it needs it
7503             var moved = false;
7504             // first validate right/bottom
7505             if((x + w) > vw+s.left){
7506                 x = vw - w - this.shadowOffset;
7507                 moved = true;
7508             }
7509             if((y + h) > vh+s.top){
7510                 y = vh - h - this.shadowOffset;
7511                 moved = true;
7512             }
7513             // then make sure top/left isn't negative
7514             if(x < s.left){
7515                 x = s.left;
7516                 moved = true;
7517             }
7518             if(y < s.top){
7519                 y = s.top;
7520                 moved = true;
7521             }
7522             if(moved){
7523                 if(this.avoidY){
7524                     var ay = this.avoidY;
7525                     if(y <= ay && (y+h) >= ay){
7526                         y = ay-h-5;   
7527                     }
7528                 }
7529                 xy = [x, y];
7530                 this.storeXY(xy);
7531                 supr.setXY.call(this, xy);
7532                 this.sync();
7533             }
7534         }
7535     },
7536
7537     isVisible : function(){
7538         return this.visible;    
7539     },
7540
7541     // private
7542     showAction : function(){
7543         this.visible = true; // track visibility to prevent getStyle calls
7544         if(this.useDisplay === true){
7545             this.setDisplayed("");
7546         }else if(this.lastXY){
7547             supr.setXY.call(this, this.lastXY);
7548         }else if(this.lastLT){
7549             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7550         }
7551     },
7552
7553     // private
7554     hideAction : function(){
7555         this.visible = false;
7556         if(this.useDisplay === true){
7557             this.setDisplayed(false);
7558         }else{
7559             this.setLeftTop(-10000,-10000);
7560         }
7561     },
7562
7563     // overridden Element method
7564     setVisible : function(v, a, d, c, e){
7565         if(v){
7566             this.showAction();
7567         }
7568         if(a && v){
7569             var cb = function(){
7570                 this.sync(true);
7571                 if(c){
7572                     c();
7573                 }
7574             }.createDelegate(this);
7575             supr.setVisible.call(this, true, true, d, cb, e);
7576         }else{
7577             if(!v){
7578                 this.hideUnders(true);
7579             }
7580             var cb = c;
7581             if(a){
7582                 cb = function(){
7583                     this.hideAction();
7584                     if(c){
7585                         c();
7586                     }
7587                 }.createDelegate(this);
7588             }
7589             supr.setVisible.call(this, v, a, d, cb, e);
7590             if(v){
7591                 this.sync(true);
7592             }else if(!a){
7593                 this.hideAction();
7594             }
7595         }
7596     },
7597
7598     storeXY : function(xy){
7599         delete this.lastLT;
7600         this.lastXY = xy;
7601     },
7602
7603     storeLeftTop : function(left, top){
7604         delete this.lastXY;
7605         this.lastLT = [left, top];
7606     },
7607
7608     // private
7609     beforeFx : function(){
7610         this.beforeAction();
7611         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7612     },
7613
7614     // private
7615     afterFx : function(){
7616         Roo.Layer.superclass.afterFx.apply(this, arguments);
7617         this.sync(this.isVisible());
7618     },
7619
7620     // private
7621     beforeAction : function(){
7622         if(!this.updating && this.shadow){
7623             this.shadow.hide();
7624         }
7625     },
7626
7627     // overridden Element method
7628     setLeft : function(left){
7629         this.storeLeftTop(left, this.getTop(true));
7630         supr.setLeft.apply(this, arguments);
7631         this.sync();
7632     },
7633
7634     setTop : function(top){
7635         this.storeLeftTop(this.getLeft(true), top);
7636         supr.setTop.apply(this, arguments);
7637         this.sync();
7638     },
7639
7640     setLeftTop : function(left, top){
7641         this.storeLeftTop(left, top);
7642         supr.setLeftTop.apply(this, arguments);
7643         this.sync();
7644     },
7645
7646     setXY : function(xy, a, d, c, e){
7647         this.fixDisplay();
7648         this.beforeAction();
7649         this.storeXY(xy);
7650         var cb = this.createCB(c);
7651         supr.setXY.call(this, xy, a, d, cb, e);
7652         if(!a){
7653             cb();
7654         }
7655     },
7656
7657     // private
7658     createCB : function(c){
7659         var el = this;
7660         return function(){
7661             el.constrainXY();
7662             el.sync(true);
7663             if(c){
7664                 c();
7665             }
7666         };
7667     },
7668
7669     // overridden Element method
7670     setX : function(x, a, d, c, e){
7671         this.setXY([x, this.getY()], a, d, c, e);
7672     },
7673
7674     // overridden Element method
7675     setY : function(y, a, d, c, e){
7676         this.setXY([this.getX(), y], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setSize : function(w, h, a, d, c, e){
7681         this.beforeAction();
7682         var cb = this.createCB(c);
7683         supr.setSize.call(this, w, h, a, d, cb, e);
7684         if(!a){
7685             cb();
7686         }
7687     },
7688
7689     // overridden Element method
7690     setWidth : function(w, a, d, c, e){
7691         this.beforeAction();
7692         var cb = this.createCB(c);
7693         supr.setWidth.call(this, w, a, d, cb, e);
7694         if(!a){
7695             cb();
7696         }
7697     },
7698
7699     // overridden Element method
7700     setHeight : function(h, a, d, c, e){
7701         this.beforeAction();
7702         var cb = this.createCB(c);
7703         supr.setHeight.call(this, h, a, d, cb, e);
7704         if(!a){
7705             cb();
7706         }
7707     },
7708
7709     // overridden Element method
7710     setBounds : function(x, y, w, h, a, d, c, e){
7711         this.beforeAction();
7712         var cb = this.createCB(c);
7713         if(!a){
7714             this.storeXY([x, y]);
7715             supr.setXY.call(this, [x, y]);
7716             supr.setSize.call(this, w, h, a, d, cb, e);
7717             cb();
7718         }else{
7719             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7720         }
7721         return this;
7722     },
7723     
7724     /**
7725      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7726      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7727      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7728      * @param {Number} zindex The new z-index to set
7729      * @return {this} The Layer
7730      */
7731     setZIndex : function(zindex){
7732         this.zindex = zindex;
7733         this.setStyle("z-index", zindex + 2);
7734         if(this.shadow){
7735             this.shadow.setZIndex(zindex + 1);
7736         }
7737         if(this.shim){
7738             this.shim.setStyle("z-index", zindex);
7739         }
7740     }
7741 });
7742 })();/*
7743  * Based on:
7744  * Ext JS Library 1.1.1
7745  * Copyright(c) 2006-2007, Ext JS, LLC.
7746  *
7747  * Originally Released Under LGPL - original licence link has changed is not relivant.
7748  *
7749  * Fork - LGPL
7750  * <script type="text/javascript">
7751  */
7752
7753
7754 /**
7755  * @class Roo.Shadow
7756  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7757  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7758  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7759  * @constructor
7760  * Create a new Shadow
7761  * @param {Object} config The config object
7762  */
7763 Roo.Shadow = function(config){
7764     Roo.apply(this, config);
7765     if(typeof this.mode != "string"){
7766         this.mode = this.defaultMode;
7767     }
7768     var o = this.offset, a = {h: 0};
7769     var rad = Math.floor(this.offset/2);
7770     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7771         case "drop":
7772             a.w = 0;
7773             a.l = a.t = o;
7774             a.t -= 1;
7775             if(Roo.isIE){
7776                 a.l -= this.offset + rad;
7777                 a.t -= this.offset + rad;
7778                 a.w -= rad;
7779                 a.h -= rad;
7780                 a.t += 1;
7781             }
7782         break;
7783         case "sides":
7784             a.w = (o*2);
7785             a.l = -o;
7786             a.t = o-1;
7787             if(Roo.isIE){
7788                 a.l -= (this.offset - rad);
7789                 a.t -= this.offset + rad;
7790                 a.l += 1;
7791                 a.w -= (this.offset - rad)*2;
7792                 a.w -= rad + 1;
7793                 a.h -= 1;
7794             }
7795         break;
7796         case "frame":
7797             a.w = a.h = (o*2);
7798             a.l = a.t = -o;
7799             a.t += 1;
7800             a.h -= 2;
7801             if(Roo.isIE){
7802                 a.l -= (this.offset - rad);
7803                 a.t -= (this.offset - rad);
7804                 a.l += 1;
7805                 a.w -= (this.offset + rad + 1);
7806                 a.h -= (this.offset + rad);
7807                 a.h += 1;
7808             }
7809         break;
7810     };
7811
7812     this.adjusts = a;
7813 };
7814
7815 Roo.Shadow.prototype = {
7816     /**
7817      * @cfg {String} mode
7818      * The shadow display mode.  Supports the following options:<br />
7819      * sides: Shadow displays on both sides and bottom only<br />
7820      * frame: Shadow displays equally on all four sides<br />
7821      * drop: Traditional bottom-right drop shadow (default)
7822      */
7823     /**
7824      * @cfg {String} offset
7825      * The number of pixels to offset the shadow from the element (defaults to 4)
7826      */
7827     offset: 4,
7828
7829     // private
7830     defaultMode: "drop",
7831
7832     /**
7833      * Displays the shadow under the target element
7834      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7835      */
7836     show : function(target){
7837         target = Roo.get(target);
7838         if(!this.el){
7839             this.el = Roo.Shadow.Pool.pull();
7840             if(this.el.dom.nextSibling != target.dom){
7841                 this.el.insertBefore(target);
7842             }
7843         }
7844         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7845         if(Roo.isIE){
7846             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7847         }
7848         this.realign(
7849             target.getLeft(true),
7850             target.getTop(true),
7851             target.getWidth(),
7852             target.getHeight()
7853         );
7854         this.el.dom.style.display = "block";
7855     },
7856
7857     /**
7858      * Returns true if the shadow is visible, else false
7859      */
7860     isVisible : function(){
7861         return this.el ? true : false;  
7862     },
7863
7864     /**
7865      * Direct alignment when values are already available. Show must be called at least once before
7866      * calling this method to ensure it is initialized.
7867      * @param {Number} left The target element left position
7868      * @param {Number} top The target element top position
7869      * @param {Number} width The target element width
7870      * @param {Number} height The target element height
7871      */
7872     realign : function(l, t, w, h){
7873         if(!this.el){
7874             return;
7875         }
7876         var a = this.adjusts, d = this.el.dom, s = d.style;
7877         var iea = 0;
7878         s.left = (l+a.l)+"px";
7879         s.top = (t+a.t)+"px";
7880         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7881  
7882         if(s.width != sws || s.height != shs){
7883             s.width = sws;
7884             s.height = shs;
7885             if(!Roo.isIE){
7886                 var cn = d.childNodes;
7887                 var sww = Math.max(0, (sw-12))+"px";
7888                 cn[0].childNodes[1].style.width = sww;
7889                 cn[1].childNodes[1].style.width = sww;
7890                 cn[2].childNodes[1].style.width = sww;
7891                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7892             }
7893         }
7894     },
7895
7896     /**
7897      * Hides this shadow
7898      */
7899     hide : function(){
7900         if(this.el){
7901             this.el.dom.style.display = "none";
7902             Roo.Shadow.Pool.push(this.el);
7903             delete this.el;
7904         }
7905     },
7906
7907     /**
7908      * Adjust the z-index of this shadow
7909      * @param {Number} zindex The new z-index
7910      */
7911     setZIndex : function(z){
7912         this.zIndex = z;
7913         if(this.el){
7914             this.el.setStyle("z-index", z);
7915         }
7916     }
7917 };
7918
7919 // Private utility class that manages the internal Shadow cache
7920 Roo.Shadow.Pool = function(){
7921     var p = [];
7922     var markup = Roo.isIE ?
7923                  '<div class="x-ie-shadow"></div>' :
7924                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
7925     return {
7926         pull : function(){
7927             var sh = p.shift();
7928             if(!sh){
7929                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7930                 sh.autoBoxAdjust = false;
7931             }
7932             return sh;
7933         },
7934
7935         push : function(sh){
7936             p.push(sh);
7937         }
7938     };
7939 }();/*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949
7950
7951 /**
7952  * @class Roo.SplitBar
7953  * @extends Roo.util.Observable
7954  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7955  * <br><br>
7956  * Usage:
7957  * <pre><code>
7958 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7959                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7960 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7961 split.minSize = 100;
7962 split.maxSize = 600;
7963 split.animate = true;
7964 split.on('moved', splitterMoved);
7965 </code></pre>
7966  * @constructor
7967  * Create a new SplitBar
7968  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7969  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7970  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7971  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7972                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7973                         position of the SplitBar).
7974  */
7975 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7976     
7977     /** @private */
7978     this.el = Roo.get(dragElement, true);
7979     this.el.dom.unselectable = "on";
7980     /** @private */
7981     this.resizingEl = Roo.get(resizingElement, true);
7982
7983     /**
7984      * @private
7985      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7986      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7987      * @type Number
7988      */
7989     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7990     
7991     /**
7992      * The minimum size of the resizing element. (Defaults to 0)
7993      * @type Number
7994      */
7995     this.minSize = 0;
7996     
7997     /**
7998      * The maximum size of the resizing element. (Defaults to 2000)
7999      * @type Number
8000      */
8001     this.maxSize = 2000;
8002     
8003     /**
8004      * Whether to animate the transition to the new size
8005      * @type Boolean
8006      */
8007     this.animate = false;
8008     
8009     /**
8010      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8011      * @type Boolean
8012      */
8013     this.useShim = false;
8014     
8015     /** @private */
8016     this.shim = null;
8017     
8018     if(!existingProxy){
8019         /** @private */
8020         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8021     }else{
8022         this.proxy = Roo.get(existingProxy).dom;
8023     }
8024     /** @private */
8025     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8026     
8027     /** @private */
8028     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8029     
8030     /** @private */
8031     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8032     
8033     /** @private */
8034     this.dragSpecs = {};
8035     
8036     /**
8037      * @private The adapter to use to positon and resize elements
8038      */
8039     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8040     this.adapter.init(this);
8041     
8042     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8043         /** @private */
8044         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8045         this.el.addClass("x-splitbar-h");
8046     }else{
8047         /** @private */
8048         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8049         this.el.addClass("x-splitbar-v");
8050     }
8051     
8052     this.addEvents({
8053         /**
8054          * @event resize
8055          * Fires when the splitter is moved (alias for {@link #event-moved})
8056          * @param {Roo.SplitBar} this
8057          * @param {Number} newSize the new width or height
8058          */
8059         "resize" : true,
8060         /**
8061          * @event moved
8062          * Fires when the splitter is moved
8063          * @param {Roo.SplitBar} this
8064          * @param {Number} newSize the new width or height
8065          */
8066         "moved" : true,
8067         /**
8068          * @event beforeresize
8069          * Fires before the splitter is dragged
8070          * @param {Roo.SplitBar} this
8071          */
8072         "beforeresize" : true,
8073
8074         "beforeapply" : true
8075     });
8076
8077     Roo.util.Observable.call(this);
8078 };
8079
8080 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8081     onStartProxyDrag : function(x, y){
8082         this.fireEvent("beforeresize", this);
8083         if(!this.overlay){
8084             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8085             o.unselectable();
8086             o.enableDisplayMode("block");
8087             // all splitbars share the same overlay
8088             Roo.SplitBar.prototype.overlay = o;
8089         }
8090         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8091         this.overlay.show();
8092         Roo.get(this.proxy).setDisplayed("block");
8093         var size = this.adapter.getElementSize(this);
8094         this.activeMinSize = this.getMinimumSize();;
8095         this.activeMaxSize = this.getMaximumSize();;
8096         var c1 = size - this.activeMinSize;
8097         var c2 = Math.max(this.activeMaxSize - size, 0);
8098         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8099             this.dd.resetConstraints();
8100             this.dd.setXConstraint(
8101                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8102                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8103             );
8104             this.dd.setYConstraint(0, 0);
8105         }else{
8106             this.dd.resetConstraints();
8107             this.dd.setXConstraint(0, 0);
8108             this.dd.setYConstraint(
8109                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8110                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8111             );
8112          }
8113         this.dragSpecs.startSize = size;
8114         this.dragSpecs.startPoint = [x, y];
8115         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8116     },
8117     
8118     /** 
8119      * @private Called after the drag operation by the DDProxy
8120      */
8121     onEndProxyDrag : function(e){
8122         Roo.get(this.proxy).setDisplayed(false);
8123         var endPoint = Roo.lib.Event.getXY(e);
8124         if(this.overlay){
8125             this.overlay.hide();
8126         }
8127         var newSize;
8128         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8129             newSize = this.dragSpecs.startSize + 
8130                 (this.placement == Roo.SplitBar.LEFT ?
8131                     endPoint[0] - this.dragSpecs.startPoint[0] :
8132                     this.dragSpecs.startPoint[0] - endPoint[0]
8133                 );
8134         }else{
8135             newSize = this.dragSpecs.startSize + 
8136                 (this.placement == Roo.SplitBar.TOP ?
8137                     endPoint[1] - this.dragSpecs.startPoint[1] :
8138                     this.dragSpecs.startPoint[1] - endPoint[1]
8139                 );
8140         }
8141         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8142         if(newSize != this.dragSpecs.startSize){
8143             if(this.fireEvent('beforeapply', this, newSize) !== false){
8144                 this.adapter.setElementSize(this, newSize);
8145                 this.fireEvent("moved", this, newSize);
8146                 this.fireEvent("resize", this, newSize);
8147             }
8148         }
8149     },
8150     
8151     /**
8152      * Get the adapter this SplitBar uses
8153      * @return The adapter object
8154      */
8155     getAdapter : function(){
8156         return this.adapter;
8157     },
8158     
8159     /**
8160      * Set the adapter this SplitBar uses
8161      * @param {Object} adapter A SplitBar adapter object
8162      */
8163     setAdapter : function(adapter){
8164         this.adapter = adapter;
8165         this.adapter.init(this);
8166     },
8167     
8168     /**
8169      * Gets the minimum size for the resizing element
8170      * @return {Number} The minimum size
8171      */
8172     getMinimumSize : function(){
8173         return this.minSize;
8174     },
8175     
8176     /**
8177      * Sets the minimum size for the resizing element
8178      * @param {Number} minSize The minimum size
8179      */
8180     setMinimumSize : function(minSize){
8181         this.minSize = minSize;
8182     },
8183     
8184     /**
8185      * Gets the maximum size for the resizing element
8186      * @return {Number} The maximum size
8187      */
8188     getMaximumSize : function(){
8189         return this.maxSize;
8190     },
8191     
8192     /**
8193      * Sets the maximum size for the resizing element
8194      * @param {Number} maxSize The maximum size
8195      */
8196     setMaximumSize : function(maxSize){
8197         this.maxSize = maxSize;
8198     },
8199     
8200     /**
8201      * Sets the initialize size for the resizing element
8202      * @param {Number} size The initial size
8203      */
8204     setCurrentSize : function(size){
8205         var oldAnimate = this.animate;
8206         this.animate = false;
8207         this.adapter.setElementSize(this, size);
8208         this.animate = oldAnimate;
8209     },
8210     
8211     /**
8212      * Destroy this splitbar. 
8213      * @param {Boolean} removeEl True to remove the element
8214      */
8215     destroy : function(removeEl){
8216         if(this.shim){
8217             this.shim.remove();
8218         }
8219         this.dd.unreg();
8220         this.proxy.parentNode.removeChild(this.proxy);
8221         if(removeEl){
8222             this.el.remove();
8223         }
8224     }
8225 });
8226
8227 /**
8228  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8229  */
8230 Roo.SplitBar.createProxy = function(dir){
8231     var proxy = new Roo.Element(document.createElement("div"));
8232     proxy.unselectable();
8233     var cls = 'x-splitbar-proxy';
8234     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8235     document.body.appendChild(proxy.dom);
8236     return proxy.dom;
8237 };
8238
8239 /** 
8240  * @class Roo.SplitBar.BasicLayoutAdapter
8241  * Default Adapter. It assumes the splitter and resizing element are not positioned
8242  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8243  */
8244 Roo.SplitBar.BasicLayoutAdapter = function(){
8245 };
8246
8247 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8248     // do nothing for now
8249     init : function(s){
8250     
8251     },
8252     /**
8253      * Called before drag operations to get the current size of the resizing element. 
8254      * @param {Roo.SplitBar} s The SplitBar using this adapter
8255      */
8256      getElementSize : function(s){
8257         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8258             return s.resizingEl.getWidth();
8259         }else{
8260             return s.resizingEl.getHeight();
8261         }
8262     },
8263     
8264     /**
8265      * Called after drag operations to set the size of the resizing element.
8266      * @param {Roo.SplitBar} s The SplitBar using this adapter
8267      * @param {Number} newSize The new size to set
8268      * @param {Function} onComplete A function to be invoked when resizing is complete
8269      */
8270     setElementSize : function(s, newSize, onComplete){
8271         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8272             if(!s.animate){
8273                 s.resizingEl.setWidth(newSize);
8274                 if(onComplete){
8275                     onComplete(s, newSize);
8276                 }
8277             }else{
8278                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8279             }
8280         }else{
8281             
8282             if(!s.animate){
8283                 s.resizingEl.setHeight(newSize);
8284                 if(onComplete){
8285                     onComplete(s, newSize);
8286                 }
8287             }else{
8288                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8289             }
8290         }
8291     }
8292 };
8293
8294 /** 
8295  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8296  * @extends Roo.SplitBar.BasicLayoutAdapter
8297  * Adapter that  moves the splitter element to align with the resized sizing element. 
8298  * Used with an absolute positioned SplitBar.
8299  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8300  * document.body, make sure you assign an id to the body element.
8301  */
8302 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8303     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8304     this.container = Roo.get(container);
8305 };
8306
8307 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8308     init : function(s){
8309         this.basic.init(s);
8310     },
8311     
8312     getElementSize : function(s){
8313         return this.basic.getElementSize(s);
8314     },
8315     
8316     setElementSize : function(s, newSize, onComplete){
8317         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8318     },
8319     
8320     moveSplitter : function(s){
8321         var yes = Roo.SplitBar;
8322         switch(s.placement){
8323             case yes.LEFT:
8324                 s.el.setX(s.resizingEl.getRight());
8325                 break;
8326             case yes.RIGHT:
8327                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8328                 break;
8329             case yes.TOP:
8330                 s.el.setY(s.resizingEl.getBottom());
8331                 break;
8332             case yes.BOTTOM:
8333                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8334                 break;
8335         }
8336     }
8337 };
8338
8339 /**
8340  * Orientation constant - Create a vertical SplitBar
8341  * @static
8342  * @type Number
8343  */
8344 Roo.SplitBar.VERTICAL = 1;
8345
8346 /**
8347  * Orientation constant - Create a horizontal SplitBar
8348  * @static
8349  * @type Number
8350  */
8351 Roo.SplitBar.HORIZONTAL = 2;
8352
8353 /**
8354  * Placement constant - The resizing element is to the left of the splitter element
8355  * @static
8356  * @type Number
8357  */
8358 Roo.SplitBar.LEFT = 1;
8359
8360 /**
8361  * Placement constant - The resizing element is to the right of the splitter element
8362  * @static
8363  * @type Number
8364  */
8365 Roo.SplitBar.RIGHT = 2;
8366
8367 /**
8368  * Placement constant - The resizing element is positioned above the splitter element
8369  * @static
8370  * @type Number
8371  */
8372 Roo.SplitBar.TOP = 3;
8373
8374 /**
8375  * Placement constant - The resizing element is positioned under splitter element
8376  * @static
8377  * @type Number
8378  */
8379 Roo.SplitBar.BOTTOM = 4;
8380 /*
8381  * Based on:
8382  * Ext JS Library 1.1.1
8383  * Copyright(c) 2006-2007, Ext JS, LLC.
8384  *
8385  * Originally Released Under LGPL - original licence link has changed is not relivant.
8386  *
8387  * Fork - LGPL
8388  * <script type="text/javascript">
8389  */
8390
8391 /**
8392  * @class Roo.View
8393  * @extends Roo.util.Observable
8394  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8395  * This class also supports single and multi selection modes. <br>
8396  * Create a data model bound view:
8397  <pre><code>
8398  var store = new Roo.data.Store(...);
8399
8400  var view = new Roo.View({
8401     el : "my-element",
8402     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8403  
8404     singleSelect: true,
8405     selectedClass: "ydataview-selected",
8406     store: store
8407  });
8408
8409  // listen for node click?
8410  view.on("click", function(vw, index, node, e){
8411  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8412  });
8413
8414  // load XML data
8415  dataModel.load("foobar.xml");
8416  </code></pre>
8417  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8418  * <br><br>
8419  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8420  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8421  * 
8422  * Note: old style constructor is still suported (container, template, config)
8423  * 
8424  * @constructor
8425  * Create a new View
8426  * @param {Object} config The config object
8427  * 
8428  */
8429 Roo.View = function(config, depreciated_tpl, depreciated_config){
8430     
8431     this.parent = false;
8432     
8433     if (typeof(depreciated_tpl) == 'undefined') {
8434         // new way.. - universal constructor.
8435         Roo.apply(this, config);
8436         this.el  = Roo.get(this.el);
8437     } else {
8438         // old format..
8439         this.el  = Roo.get(config);
8440         this.tpl = depreciated_tpl;
8441         Roo.apply(this, depreciated_config);
8442     }
8443     this.wrapEl  = this.el.wrap().wrap();
8444     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8445     
8446     
8447     if(typeof(this.tpl) == "string"){
8448         this.tpl = new Roo.Template(this.tpl);
8449     } else {
8450         // support xtype ctors..
8451         this.tpl = new Roo.factory(this.tpl, Roo);
8452     }
8453     
8454     
8455     this.tpl.compile();
8456     
8457     /** @private */
8458     this.addEvents({
8459         /**
8460          * @event beforeclick
8461          * Fires before a click is processed. Returns false to cancel the default action.
8462          * @param {Roo.View} this
8463          * @param {Number} index The index of the target node
8464          * @param {HTMLElement} node The target node
8465          * @param {Roo.EventObject} e The raw event object
8466          */
8467             "beforeclick" : true,
8468         /**
8469          * @event click
8470          * Fires when a template node is clicked.
8471          * @param {Roo.View} this
8472          * @param {Number} index The index of the target node
8473          * @param {HTMLElement} node The target node
8474          * @param {Roo.EventObject} e The raw event object
8475          */
8476             "click" : true,
8477         /**
8478          * @event dblclick
8479          * Fires when a template node is double clicked.
8480          * @param {Roo.View} this
8481          * @param {Number} index The index of the target node
8482          * @param {HTMLElement} node The target node
8483          * @param {Roo.EventObject} e The raw event object
8484          */
8485             "dblclick" : true,
8486         /**
8487          * @event contextmenu
8488          * Fires when a template node is right clicked.
8489          * @param {Roo.View} this
8490          * @param {Number} index The index of the target node
8491          * @param {HTMLElement} node The target node
8492          * @param {Roo.EventObject} e The raw event object
8493          */
8494             "contextmenu" : true,
8495         /**
8496          * @event selectionchange
8497          * Fires when the selected nodes change.
8498          * @param {Roo.View} this
8499          * @param {Array} selections Array of the selected nodes
8500          */
8501             "selectionchange" : true,
8502     
8503         /**
8504          * @event beforeselect
8505          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506          * @param {Roo.View} this
8507          * @param {HTMLElement} node The node to be selected
8508          * @param {Array} selections Array of currently selected nodes
8509          */
8510             "beforeselect" : true,
8511         /**
8512          * @event preparedata
8513          * Fires on every row to render, to allow you to change the data.
8514          * @param {Roo.View} this
8515          * @param {Object} data to be rendered (change this)
8516          */
8517           "preparedata" : true
8518           
8519           
8520         });
8521
8522
8523
8524     this.el.on({
8525         "click": this.onClick,
8526         "dblclick": this.onDblClick,
8527         "contextmenu": this.onContextMenu,
8528         scope:this
8529     });
8530
8531     this.selections = [];
8532     this.nodes = [];
8533     this.cmp = new Roo.CompositeElementLite([]);
8534     if(this.store){
8535         this.store = Roo.factory(this.store, Roo.data);
8536         this.setStore(this.store, true);
8537     }
8538     
8539     if ( this.footer && this.footer.xtype) {
8540            
8541          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8542         
8543         this.footer.dataSource = this.store
8544         this.footer.container = fctr;
8545         this.footer = Roo.factory(this.footer, Roo);
8546         fctr.insertFirst(this.el);
8547         
8548         // this is a bit insane - as the paging toolbar seems to detach the el..
8549 //        dom.parentNode.parentNode.parentNode
8550          // they get detached?
8551     }
8552     
8553     
8554     Roo.View.superclass.constructor.call(this);
8555     
8556     
8557 };
8558
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8560     
8561      /**
8562      * @cfg {Roo.data.Store} store Data store to load data from.
8563      */
8564     store : false,
8565     
8566     /**
8567      * @cfg {String|Roo.Element} el The container element.
8568      */
8569     el : '',
8570     
8571     /**
8572      * @cfg {String|Roo.Template} tpl The template used by this View 
8573      */
8574     tpl : false,
8575     /**
8576      * @cfg {String} dataName the named area of the template to use as the data area
8577      *                          Works with domtemplates roo-name="name"
8578      */
8579     dataName: false,
8580     /**
8581      * @cfg {String} selectedClass The css class to add to selected nodes
8582      */
8583     selectedClass : "x-view-selected",
8584      /**
8585      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8586      */
8587     emptyText : "",
8588     
8589     /**
8590      * @cfg {String} text to display on mask (default Loading)
8591      */
8592     mask : false,
8593     /**
8594      * @cfg {Boolean} multiSelect Allow multiple selection
8595      */
8596     multiSelect : false,
8597     /**
8598      * @cfg {Boolean} singleSelect Allow single selection
8599      */
8600     singleSelect:  false,
8601     
8602     /**
8603      * @cfg {Boolean} toggleSelect - selecting 
8604      */
8605     toggleSelect : false,
8606     
8607     /**
8608      * @cfg {Boolean} tickable - selecting 
8609      */
8610     tickable : false,
8611     
8612     /**
8613      * Returns the element this view is bound to.
8614      * @return {Roo.Element}
8615      */
8616     getEl : function(){
8617         return this.wrapEl;
8618     },
8619     
8620     
8621
8622     /**
8623      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8624      */
8625     refresh : function(){
8626         Roo.log('refresh');
8627         var t = this.tpl;
8628         
8629         // if we are using something like 'domtemplate', then
8630         // the what gets used is:
8631         // t.applySubtemplate(NAME, data, wrapping data..)
8632         // the outer template then get' applied with
8633         //     the store 'extra data'
8634         // and the body get's added to the
8635         //      roo-name="data" node?
8636         //      <span class='roo-tpl-{name}'></span> ?????
8637         
8638         
8639         
8640         this.clearSelections();
8641         this.el.update("");
8642         var html = [];
8643         var records = this.store.getRange();
8644         if(records.length < 1) {
8645             
8646             // is this valid??  = should it render a template??
8647             
8648             this.el.update(this.emptyText);
8649             return;
8650         }
8651         var el = this.el;
8652         if (this.dataName) {
8653             this.el.update(t.apply(this.store.meta)); //????
8654             el = this.el.child('.roo-tpl-' + this.dataName);
8655         }
8656         
8657         for(var i = 0, len = records.length; i < len; i++){
8658             var data = this.prepareData(records[i].data, i, records[i]);
8659             this.fireEvent("preparedata", this, data, i, records[i]);
8660             
8661             var d = Roo.apply({}, data);
8662             
8663             if(this.tickable){
8664                 Roo.apply(d, {'roo-id' : Roo.id()});
8665                 
8666                 var _this = this;
8667             
8668                 Roo.each(this.parent.item, function(item){
8669                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8670                         return;
8671                     }
8672                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8673                 });
8674             }
8675             
8676             html[html.length] = Roo.util.Format.trim(
8677                 this.dataName ?
8678                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8679                     t.apply(d)
8680             );
8681         }
8682         
8683         
8684         
8685         el.update(html.join(""));
8686         this.nodes = el.dom.childNodes;
8687         this.updateIndexes(0);
8688     },
8689     
8690
8691     /**
8692      * Function to override to reformat the data that is sent to
8693      * the template for each node.
8694      * DEPRICATED - use the preparedata event handler.
8695      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696      * a JSON object for an UpdateManager bound view).
8697      */
8698     prepareData : function(data, index, record)
8699     {
8700         this.fireEvent("preparedata", this, data, index, record);
8701         return data;
8702     },
8703
8704     onUpdate : function(ds, record){
8705          Roo.log('on update');   
8706         this.clearSelections();
8707         var index = this.store.indexOf(record);
8708         var n = this.nodes[index];
8709         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710         n.parentNode.removeChild(n);
8711         this.updateIndexes(index, index);
8712     },
8713
8714     
8715     
8716 // --------- FIXME     
8717     onAdd : function(ds, records, index)
8718     {
8719         Roo.log(['on Add', ds, records, index] );        
8720         this.clearSelections();
8721         if(this.nodes.length == 0){
8722             this.refresh();
8723             return;
8724         }
8725         var n = this.nodes[index];
8726         for(var i = 0, len = records.length; i < len; i++){
8727             var d = this.prepareData(records[i].data, i, records[i]);
8728             if(n){
8729                 this.tpl.insertBefore(n, d);
8730             }else{
8731                 
8732                 this.tpl.append(this.el, d);
8733             }
8734         }
8735         this.updateIndexes(index);
8736     },
8737
8738     onRemove : function(ds, record, index){
8739         Roo.log('onRemove');
8740         this.clearSelections();
8741         var el = this.dataName  ?
8742             this.el.child('.roo-tpl-' + this.dataName) :
8743             this.el; 
8744         
8745         el.dom.removeChild(this.nodes[index]);
8746         this.updateIndexes(index);
8747     },
8748
8749     /**
8750      * Refresh an individual node.
8751      * @param {Number} index
8752      */
8753     refreshNode : function(index){
8754         this.onUpdate(this.store, this.store.getAt(index));
8755     },
8756
8757     updateIndexes : function(startIndex, endIndex){
8758         var ns = this.nodes;
8759         startIndex = startIndex || 0;
8760         endIndex = endIndex || ns.length - 1;
8761         for(var i = startIndex; i <= endIndex; i++){
8762             ns[i].nodeIndex = i;
8763         }
8764     },
8765
8766     /**
8767      * Changes the data store this view uses and refresh the view.
8768      * @param {Store} store
8769      */
8770     setStore : function(store, initial){
8771         if(!initial && this.store){
8772             this.store.un("datachanged", this.refresh);
8773             this.store.un("add", this.onAdd);
8774             this.store.un("remove", this.onRemove);
8775             this.store.un("update", this.onUpdate);
8776             this.store.un("clear", this.refresh);
8777             this.store.un("beforeload", this.onBeforeLoad);
8778             this.store.un("load", this.onLoad);
8779             this.store.un("loadexception", this.onLoad);
8780         }
8781         if(store){
8782           
8783             store.on("datachanged", this.refresh, this);
8784             store.on("add", this.onAdd, this);
8785             store.on("remove", this.onRemove, this);
8786             store.on("update", this.onUpdate, this);
8787             store.on("clear", this.refresh, this);
8788             store.on("beforeload", this.onBeforeLoad, this);
8789             store.on("load", this.onLoad, this);
8790             store.on("loadexception", this.onLoad, this);
8791         }
8792         
8793         if(store){
8794             this.refresh();
8795         }
8796     },
8797     /**
8798      * onbeforeLoad - masks the loading area.
8799      *
8800      */
8801     onBeforeLoad : function(store,opts)
8802     {
8803          Roo.log('onBeforeLoad');   
8804         if (!opts.add) {
8805             this.el.update("");
8806         }
8807         this.el.mask(this.mask ? this.mask : "Loading" ); 
8808     },
8809     onLoad : function ()
8810     {
8811         this.el.unmask();
8812     },
8813     
8814
8815     /**
8816      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817      * @param {HTMLElement} node
8818      * @return {HTMLElement} The template node
8819      */
8820     findItemFromChild : function(node){
8821         var el = this.dataName  ?
8822             this.el.child('.roo-tpl-' + this.dataName,true) :
8823             this.el.dom; 
8824         
8825         if(!node || node.parentNode == el){
8826                     return node;
8827             }
8828             var p = node.parentNode;
8829             while(p && p != el){
8830             if(p.parentNode == el){
8831                 return p;
8832             }
8833             p = p.parentNode;
8834         }
8835             return null;
8836     },
8837
8838     /** @ignore */
8839     onClick : function(e){
8840         var item = this.findItemFromChild(e.getTarget());
8841         if(item){
8842             var index = this.indexOf(item);
8843             if(this.onItemClick(item, index, e) !== false){
8844                 this.fireEvent("click", this, index, item, e);
8845             }
8846         }else{
8847             this.clearSelections();
8848         }
8849     },
8850
8851     /** @ignore */
8852     onContextMenu : function(e){
8853         var item = this.findItemFromChild(e.getTarget());
8854         if(item){
8855             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8856         }
8857     },
8858
8859     /** @ignore */
8860     onDblClick : function(e){
8861         var item = this.findItemFromChild(e.getTarget());
8862         if(item){
8863             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8864         }
8865     },
8866
8867     onItemClick : function(item, index, e)
8868     {
8869         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8870             return false;
8871         }
8872         if (this.toggleSelect) {
8873             var m = this.isSelected(item) ? 'unselect' : 'select';
8874             Roo.log(m);
8875             var _t = this;
8876             _t[m](item, true, false);
8877             return true;
8878         }
8879         if(this.multiSelect || this.singleSelect){
8880             if(this.multiSelect && e.shiftKey && this.lastSelection){
8881                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8882             }else{
8883                 this.select(item, this.multiSelect && e.ctrlKey);
8884                 this.lastSelection = item;
8885             }
8886             
8887             if(!this.tickable){
8888                 e.preventDefault();
8889             }
8890             
8891         }
8892         return true;
8893     },
8894
8895     /**
8896      * Get the number of selected nodes.
8897      * @return {Number}
8898      */
8899     getSelectionCount : function(){
8900         return this.selections.length;
8901     },
8902
8903     /**
8904      * Get the currently selected nodes.
8905      * @return {Array} An array of HTMLElements
8906      */
8907     getSelectedNodes : function(){
8908         return this.selections;
8909     },
8910
8911     /**
8912      * Get the indexes of the selected nodes.
8913      * @return {Array}
8914      */
8915     getSelectedIndexes : function(){
8916         var indexes = [], s = this.selections;
8917         for(var i = 0, len = s.length; i < len; i++){
8918             indexes.push(s[i].nodeIndex);
8919         }
8920         return indexes;
8921     },
8922
8923     /**
8924      * Clear all selections
8925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8926      */
8927     clearSelections : function(suppressEvent){
8928         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929             this.cmp.elements = this.selections;
8930             this.cmp.removeClass(this.selectedClass);
8931             this.selections = [];
8932             if(!suppressEvent){
8933                 this.fireEvent("selectionchange", this, this.selections);
8934             }
8935         }
8936     },
8937
8938     /**
8939      * Returns true if the passed node is selected
8940      * @param {HTMLElement/Number} node The node or node index
8941      * @return {Boolean}
8942      */
8943     isSelected : function(node){
8944         var s = this.selections;
8945         if(s.length < 1){
8946             return false;
8947         }
8948         node = this.getNode(node);
8949         return s.indexOf(node) !== -1;
8950     },
8951
8952     /**
8953      * Selects nodes.
8954      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8955      * @param {Boolean} keepExisting (optional) true to keep existing selections
8956      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8957      */
8958     select : function(nodeInfo, keepExisting, suppressEvent){
8959         if(nodeInfo instanceof Array){
8960             if(!keepExisting){
8961                 this.clearSelections(true);
8962             }
8963             for(var i = 0, len = nodeInfo.length; i < len; i++){
8964                 this.select(nodeInfo[i], true, true);
8965             }
8966             return;
8967         } 
8968         var node = this.getNode(nodeInfo);
8969         if(!node || this.isSelected(node)){
8970             return; // already selected.
8971         }
8972         if(!keepExisting){
8973             this.clearSelections(true);
8974         }
8975         
8976         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977             Roo.fly(node).addClass(this.selectedClass);
8978             this.selections.push(node);
8979             if(!suppressEvent){
8980                 this.fireEvent("selectionchange", this, this.selections);
8981             }
8982         }
8983         
8984         
8985     },
8986       /**
8987      * Unselects nodes.
8988      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8989      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8991      */
8992     unselect : function(nodeInfo, keepExisting, suppressEvent)
8993     {
8994         if(nodeInfo instanceof Array){
8995             Roo.each(this.selections, function(s) {
8996                 this.unselect(s, nodeInfo);
8997             }, this);
8998             return;
8999         }
9000         var node = this.getNode(nodeInfo);
9001         if(!node || !this.isSelected(node)){
9002             Roo.log("not selected");
9003             return; // not selected.
9004         }
9005         // fireevent???
9006         var ns = [];
9007         Roo.each(this.selections, function(s) {
9008             if (s == node ) {
9009                 Roo.fly(node).removeClass(this.selectedClass);
9010
9011                 return;
9012             }
9013             ns.push(s);
9014         },this);
9015         
9016         this.selections= ns;
9017         this.fireEvent("selectionchange", this, this.selections);
9018     },
9019
9020     /**
9021      * Gets a template node.
9022      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023      * @return {HTMLElement} The node or null if it wasn't found
9024      */
9025     getNode : function(nodeInfo){
9026         if(typeof nodeInfo == "string"){
9027             return document.getElementById(nodeInfo);
9028         }else if(typeof nodeInfo == "number"){
9029             return this.nodes[nodeInfo];
9030         }
9031         return nodeInfo;
9032     },
9033
9034     /**
9035      * Gets a range template nodes.
9036      * @param {Number} startIndex
9037      * @param {Number} endIndex
9038      * @return {Array} An array of nodes
9039      */
9040     getNodes : function(start, end){
9041         var ns = this.nodes;
9042         start = start || 0;
9043         end = typeof end == "undefined" ? ns.length - 1 : end;
9044         var nodes = [];
9045         if(start <= end){
9046             for(var i = start; i <= end; i++){
9047                 nodes.push(ns[i]);
9048             }
9049         } else{
9050             for(var i = start; i >= end; i--){
9051                 nodes.push(ns[i]);
9052             }
9053         }
9054         return nodes;
9055     },
9056
9057     /**
9058      * Finds the index of the passed node
9059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060      * @return {Number} The index of the node or -1
9061      */
9062     indexOf : function(node){
9063         node = this.getNode(node);
9064         if(typeof node.nodeIndex == "number"){
9065             return node.nodeIndex;
9066         }
9067         var ns = this.nodes;
9068         for(var i = 0, len = ns.length; i < len; i++){
9069             if(ns[i] == node){
9070                 return i;
9071             }
9072         }
9073         return -1;
9074     }
9075 });
9076 /*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088  * @class Roo.JsonView
9089  * @extends Roo.View
9090  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9091 <pre><code>
9092 var view = new Roo.JsonView({
9093     container: "my-element",
9094     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9095     multiSelect: true, 
9096     jsonRoot: "data" 
9097 });
9098
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102 });
9103
9104 // direct load of JSON data
9105 view.load("foobar.php");
9106
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109     '&lt;div class="entry"&gt;' +
9110     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9111     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9112     "&lt;/div&gt;&lt;hr /&gt;"
9113 );
9114
9115 var moreView = new Roo.JsonView({
9116     container :  "entry-list", 
9117     template : tpl,
9118     jsonRoot: "posts"
9119 });
9120 moreView.on("beforerender", this.sortEntries, this);
9121 moreView.load({
9122     url: "/blog/get-posts.php",
9123     params: "allposts=true",
9124     text: "Loading Blog Entries..."
9125 });
9126 </code></pre>
9127
9128 * Note: old code is supported with arguments : (container, template, config)
9129
9130
9131  * @constructor
9132  * Create a new JsonView
9133  * 
9134  * @param {Object} config The config object
9135  * 
9136  */
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9138     
9139     
9140     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9141
9142     var um = this.el.getUpdateManager();
9143     um.setRenderer(this);
9144     um.on("update", this.onLoad, this);
9145     um.on("failure", this.onLoadException, this);
9146
9147     /**
9148      * @event beforerender
9149      * Fires before rendering of the downloaded JSON data.
9150      * @param {Roo.JsonView} this
9151      * @param {Object} data The JSON data loaded
9152      */
9153     /**
9154      * @event load
9155      * Fires when data is loaded.
9156      * @param {Roo.JsonView} this
9157      * @param {Object} data The JSON data loaded
9158      * @param {Object} response The raw Connect response object
9159      */
9160     /**
9161      * @event loadexception
9162      * Fires when loading fails.
9163      * @param {Roo.JsonView} this
9164      * @param {Object} response The raw Connect response object
9165      */
9166     this.addEvents({
9167         'beforerender' : true,
9168         'load' : true,
9169         'loadexception' : true
9170     });
9171 };
9172 Roo.extend(Roo.JsonView, Roo.View, {
9173     /**
9174      * @type {String} The root property in the loaded JSON object that contains the data
9175      */
9176     jsonRoot : "",
9177
9178     /**
9179      * Refreshes the view.
9180      */
9181     refresh : function(){
9182         this.clearSelections();
9183         this.el.update("");
9184         var html = [];
9185         var o = this.jsonData;
9186         if(o && o.length > 0){
9187             for(var i = 0, len = o.length; i < len; i++){
9188                 var data = this.prepareData(o[i], i, o);
9189                 html[html.length] = this.tpl.apply(data);
9190             }
9191         }else{
9192             html.push(this.emptyText);
9193         }
9194         this.el.update(html.join(""));
9195         this.nodes = this.el.dom.childNodes;
9196         this.updateIndexes(0);
9197     },
9198
9199     /**
9200      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9201      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9202      <pre><code>
9203      view.load({
9204          url: "your-url.php",
9205          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206          callback: yourFunction,
9207          scope: yourObject, //(optional scope)
9208          discardUrl: false,
9209          nocache: false,
9210          text: "Loading...",
9211          timeout: 30,
9212          scripts: false
9213      });
9214      </code></pre>
9215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9217      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9220      */
9221     load : function(){
9222         var um = this.el.getUpdateManager();
9223         um.update.apply(um, arguments);
9224     },
9225
9226     render : function(el, response){
9227         this.clearSelections();
9228         this.el.update("");
9229         var o;
9230         try{
9231             o = Roo.util.JSON.decode(response.responseText);
9232             if(this.jsonRoot){
9233                 
9234                 o = o[this.jsonRoot];
9235             }
9236         } catch(e){
9237         }
9238         /**
9239          * The current JSON data or null
9240          */
9241         this.jsonData = o;
9242         this.beforeRender();
9243         this.refresh();
9244     },
9245
9246 /**
9247  * Get the number of records in the current JSON dataset
9248  * @return {Number}
9249  */
9250     getCount : function(){
9251         return this.jsonData ? this.jsonData.length : 0;
9252     },
9253
9254 /**
9255  * Returns the JSON object for the specified node(s)
9256  * @param {HTMLElement/Array} node The node or an array of nodes
9257  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258  * you get the JSON object for the node
9259  */
9260     getNodeData : function(node){
9261         if(node instanceof Array){
9262             var data = [];
9263             for(var i = 0, len = node.length; i < len; i++){
9264                 data.push(this.getNodeData(node[i]));
9265             }
9266             return data;
9267         }
9268         return this.jsonData[this.indexOf(node)] || null;
9269     },
9270
9271     beforeRender : function(){
9272         this.snapshot = this.jsonData;
9273         if(this.sortInfo){
9274             this.sort.apply(this, this.sortInfo);
9275         }
9276         this.fireEvent("beforerender", this, this.jsonData);
9277     },
9278
9279     onLoad : function(el, o){
9280         this.fireEvent("load", this, this.jsonData, o);
9281     },
9282
9283     onLoadException : function(el, o){
9284         this.fireEvent("loadexception", this, o);
9285     },
9286
9287 /**
9288  * Filter the data by a specific property.
9289  * @param {String} property A property on your JSON objects
9290  * @param {String/RegExp} value Either string that the property values
9291  * should start with, or a RegExp to test against the property
9292  */
9293     filter : function(property, value){
9294         if(this.jsonData){
9295             var data = [];
9296             var ss = this.snapshot;
9297             if(typeof value == "string"){
9298                 var vlen = value.length;
9299                 if(vlen == 0){
9300                     this.clearFilter();
9301                     return;
9302                 }
9303                 value = value.toLowerCase();
9304                 for(var i = 0, len = ss.length; i < len; i++){
9305                     var o = ss[i];
9306                     if(o[property].substr(0, vlen).toLowerCase() == value){
9307                         data.push(o);
9308                     }
9309                 }
9310             } else if(value.exec){ // regex?
9311                 for(var i = 0, len = ss.length; i < len; i++){
9312                     var o = ss[i];
9313                     if(value.test(o[property])){
9314                         data.push(o);
9315                     }
9316                 }
9317             } else{
9318                 return;
9319             }
9320             this.jsonData = data;
9321             this.refresh();
9322         }
9323     },
9324
9325 /**
9326  * Filter by a function. The passed function will be called with each
9327  * object in the current dataset. If the function returns true the value is kept,
9328  * otherwise it is filtered.
9329  * @param {Function} fn
9330  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9331  */
9332     filterBy : function(fn, scope){
9333         if(this.jsonData){
9334             var data = [];
9335             var ss = this.snapshot;
9336             for(var i = 0, len = ss.length; i < len; i++){
9337                 var o = ss[i];
9338                 if(fn.call(scope || this, o)){
9339                     data.push(o);
9340                 }
9341             }
9342             this.jsonData = data;
9343             this.refresh();
9344         }
9345     },
9346
9347 /**
9348  * Clears the current filter.
9349  */
9350     clearFilter : function(){
9351         if(this.snapshot && this.jsonData != this.snapshot){
9352             this.jsonData = this.snapshot;
9353             this.refresh();
9354         }
9355     },
9356
9357
9358 /**
9359  * Sorts the data for this view and refreshes it.
9360  * @param {String} property A property on your JSON objects to sort on
9361  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9363  */
9364     sort : function(property, dir, sortType){
9365         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9366         if(this.jsonData){
9367             var p = property;
9368             var dsc = dir && dir.toLowerCase() == "desc";
9369             var f = function(o1, o2){
9370                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9372                 ;
9373                 if(v1 < v2){
9374                     return dsc ? +1 : -1;
9375                 } else if(v1 > v2){
9376                     return dsc ? -1 : +1;
9377                 } else{
9378                     return 0;
9379                 }
9380             };
9381             this.jsonData.sort(f);
9382             this.refresh();
9383             if(this.jsonData != this.snapshot){
9384                 this.snapshot.sort(f);
9385             }
9386         }
9387     }
9388 });/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398  
9399
9400 /**
9401  * @class Roo.ColorPalette
9402  * @extends Roo.Component
9403  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9404  * Here's an example of typical usage:
9405  * <pre><code>
9406 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9407 cp.render('my-div');
9408
9409 cp.on('select', function(palette, selColor){
9410     // do something with selColor
9411 });
9412 </code></pre>
9413  * @constructor
9414  * Create a new ColorPalette
9415  * @param {Object} config The config object
9416  */
9417 Roo.ColorPalette = function(config){
9418     Roo.ColorPalette.superclass.constructor.call(this, config);
9419     this.addEvents({
9420         /**
9421              * @event select
9422              * Fires when a color is selected
9423              * @param {ColorPalette} this
9424              * @param {String} color The 6-digit color hex code (without the # symbol)
9425              */
9426         select: true
9427     });
9428
9429     if(this.handler){
9430         this.on("select", this.handler, this.scope, true);
9431     }
9432 };
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9434     /**
9435      * @cfg {String} itemCls
9436      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9437      */
9438     itemCls : "x-color-palette",
9439     /**
9440      * @cfg {String} value
9441      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9442      * the hex codes are case-sensitive.
9443      */
9444     value : null,
9445     clickEvent:'click',
9446     // private
9447     ctype: "Roo.ColorPalette",
9448
9449     /**
9450      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9451      */
9452     allowReselect : false,
9453
9454     /**
9455      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9456      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9457      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458      * of colors with the width setting until the box is symmetrical.</p>
9459      * <p>You can override individual colors if needed:</p>
9460      * <pre><code>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000";  // change the first box to red
9463 </code></pre>
9464
9465 Or you can provide a custom array of your own for complete control:
9466 <pre><code>
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9469 </code></pre>
9470      * @type Array
9471      */
9472     colors : [
9473         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9478     ],
9479
9480     // private
9481     onRender : function(container, position){
9482         var t = new Roo.MasterTemplate(
9483             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9484         );
9485         var c = this.colors;
9486         for(var i = 0, len = c.length; i < len; i++){
9487             t.add([c[i]]);
9488         }
9489         var el = document.createElement("div");
9490         el.className = this.itemCls;
9491         t.overwrite(el);
9492         container.dom.insertBefore(el, position);
9493         this.el = Roo.get(el);
9494         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9495         if(this.clickEvent != 'click'){
9496             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9497         }
9498     },
9499
9500     // private
9501     afterRender : function(){
9502         Roo.ColorPalette.superclass.afterRender.call(this);
9503         if(this.value){
9504             var s = this.value;
9505             this.value = null;
9506             this.select(s);
9507         }
9508     },
9509
9510     // private
9511     handleClick : function(e, t){
9512         e.preventDefault();
9513         if(!this.disabled){
9514             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515             this.select(c.toUpperCase());
9516         }
9517     },
9518
9519     /**
9520      * Selects the specified color in the palette (fires the select event)
9521      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9522      */
9523     select : function(color){
9524         color = color.replace("#", "");
9525         if(color != this.value || this.allowReselect){
9526             var el = this.el;
9527             if(this.value){
9528                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9529             }
9530             el.child("a.color-"+color).addClass("x-color-palette-sel");
9531             this.value = color;
9532             this.fireEvent("select", this, color);
9533         }
9534     }
9535 });/*
9536  * Based on:
9537  * Ext JS Library 1.1.1
9538  * Copyright(c) 2006-2007, Ext JS, LLC.
9539  *
9540  * Originally Released Under LGPL - original licence link has changed is not relivant.
9541  *
9542  * Fork - LGPL
9543  * <script type="text/javascript">
9544  */
9545  
9546 /**
9547  * @class Roo.DatePicker
9548  * @extends Roo.Component
9549  * Simple date picker class.
9550  * @constructor
9551  * Create a new DatePicker
9552  * @param {Object} config The config object
9553  */
9554 Roo.DatePicker = function(config){
9555     Roo.DatePicker.superclass.constructor.call(this, config);
9556
9557     this.value = config && config.value ?
9558                  config.value.clearTime() : new Date().clearTime();
9559
9560     this.addEvents({
9561         /**
9562              * @event select
9563              * Fires when a date is selected
9564              * @param {DatePicker} this
9565              * @param {Date} date The selected date
9566              */
9567         'select': true,
9568         /**
9569              * @event monthchange
9570              * Fires when the displayed month changes 
9571              * @param {DatePicker} this
9572              * @param {Date} date The selected month
9573              */
9574         'monthchange': true
9575     });
9576
9577     if(this.handler){
9578         this.on("select", this.handler,  this.scope || this);
9579     }
9580     // build the disabledDatesRE
9581     if(!this.disabledDatesRE && this.disabledDates){
9582         var dd = this.disabledDates;
9583         var re = "(?:";
9584         for(var i = 0; i < dd.length; i++){
9585             re += dd[i];
9586             if(i != dd.length-1) re += "|";
9587         }
9588         this.disabledDatesRE = new RegExp(re + ")");
9589     }
9590 };
9591
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9593     /**
9594      * @cfg {String} todayText
9595      * The text to display on the button that selects the current date (defaults to "Today")
9596      */
9597     todayText : "Today",
9598     /**
9599      * @cfg {String} okText
9600      * The text to display on the ok button
9601      */
9602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9603     /**
9604      * @cfg {String} cancelText
9605      * The text to display on the cancel button
9606      */
9607     cancelText : "Cancel",
9608     /**
9609      * @cfg {String} todayTip
9610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9611      */
9612     todayTip : "{0} (Spacebar)",
9613     /**
9614      * @cfg {Date} minDate
9615      * Minimum allowable date (JavaScript date object, defaults to null)
9616      */
9617     minDate : null,
9618     /**
9619      * @cfg {Date} maxDate
9620      * Maximum allowable date (JavaScript date object, defaults to null)
9621      */
9622     maxDate : null,
9623     /**
9624      * @cfg {String} minText
9625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9626      */
9627     minText : "This date is before the minimum date",
9628     /**
9629      * @cfg {String} maxText
9630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9631      */
9632     maxText : "This date is after the maximum date",
9633     /**
9634      * @cfg {String} format
9635      * The default date format string which can be overriden for localization support.  The format must be
9636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9637      */
9638     format : "m/d/y",
9639     /**
9640      * @cfg {Array} disabledDays
9641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9642      */
9643     disabledDays : null,
9644     /**
9645      * @cfg {String} disabledDaysText
9646      * The tooltip to display when the date falls on a disabled day (defaults to "")
9647      */
9648     disabledDaysText : "",
9649     /**
9650      * @cfg {RegExp} disabledDatesRE
9651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9652      */
9653     disabledDatesRE : null,
9654     /**
9655      * @cfg {String} disabledDatesText
9656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9657      */
9658     disabledDatesText : "",
9659     /**
9660      * @cfg {Boolean} constrainToViewport
9661      * True to constrain the date picker to the viewport (defaults to true)
9662      */
9663     constrainToViewport : true,
9664     /**
9665      * @cfg {Array} monthNames
9666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9667      */
9668     monthNames : Date.monthNames,
9669     /**
9670      * @cfg {Array} dayNames
9671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9672      */
9673     dayNames : Date.dayNames,
9674     /**
9675      * @cfg {String} nextText
9676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9677      */
9678     nextText: 'Next Month (Control+Right)',
9679     /**
9680      * @cfg {String} prevText
9681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9682      */
9683     prevText: 'Previous Month (Control+Left)',
9684     /**
9685      * @cfg {String} monthYearText
9686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9687      */
9688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9689     /**
9690      * @cfg {Number} startDay
9691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9692      */
9693     startDay : 0,
9694     /**
9695      * @cfg {Bool} showClear
9696      * Show a clear button (usefull for date form elements that can be blank.)
9697      */
9698     
9699     showClear: false,
9700     
9701     /**
9702      * Sets the value of the date field
9703      * @param {Date} value The date to set
9704      */
9705     setValue : function(value){
9706         var old = this.value;
9707         
9708         if (typeof(value) == 'string') {
9709          
9710             value = Date.parseDate(value, this.format);
9711         }
9712         if (!value) {
9713             value = new Date();
9714         }
9715         
9716         this.value = value.clearTime(true);
9717         if(this.el){
9718             this.update(this.value);
9719         }
9720     },
9721
9722     /**
9723      * Gets the current selected value of the date field
9724      * @return {Date} The selected date
9725      */
9726     getValue : function(){
9727         return this.value;
9728     },
9729
9730     // private
9731     focus : function(){
9732         if(this.el){
9733             this.update(this.activeDate);
9734         }
9735     },
9736
9737     // privateval
9738     onRender : function(container, position){
9739         
9740         var m = [
9741              '<table cellspacing="0">',
9742                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
9743                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744         var dn = this.dayNames;
9745         for(var i = 0; i < 7; i++){
9746             var d = this.startDay+i;
9747             if(d > 6){
9748                 d = d-7;
9749             }
9750             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9751         }
9752         m[m.length] = "</tr></thead><tbody><tr>";
9753         for(var i = 0; i < 42; i++) {
9754             if(i % 7 == 0 && i != 0){
9755                 m[m.length] = "</tr><tr>";
9756             }
9757             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9758         }
9759         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9761
9762         var el = document.createElement("div");
9763         el.className = "x-date-picker";
9764         el.innerHTML = m.join("");
9765
9766         container.dom.insertBefore(el, position);
9767
9768         this.el = Roo.get(el);
9769         this.eventEl = Roo.get(el.firstChild);
9770
9771         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772             handler: this.showPrevMonth,
9773             scope: this,
9774             preventDefault:true,
9775             stopDefault:true
9776         });
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779             handler: this.showNextMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9786
9787         this.monthPicker = this.el.down('div.x-date-mp');
9788         this.monthPicker.enableDisplayMode('block');
9789         
9790         var kn = new Roo.KeyNav(this.eventEl, {
9791             "left" : function(e){
9792                 e.ctrlKey ?
9793                     this.showPrevMonth() :
9794                     this.update(this.activeDate.add("d", -1));
9795             },
9796
9797             "right" : function(e){
9798                 e.ctrlKey ?
9799                     this.showNextMonth() :
9800                     this.update(this.activeDate.add("d", 1));
9801             },
9802
9803             "up" : function(e){
9804                 e.ctrlKey ?
9805                     this.showNextYear() :
9806                     this.update(this.activeDate.add("d", -7));
9807             },
9808
9809             "down" : function(e){
9810                 e.ctrlKey ?
9811                     this.showPrevYear() :
9812                     this.update(this.activeDate.add("d", 7));
9813             },
9814
9815             "pageUp" : function(e){
9816                 this.showNextMonth();
9817             },
9818
9819             "pageDown" : function(e){
9820                 this.showPrevMonth();
9821             },
9822
9823             "enter" : function(e){
9824                 e.stopPropagation();
9825                 return true;
9826             },
9827
9828             scope : this
9829         });
9830
9831         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9832
9833         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9834
9835         this.el.unselectable();
9836         
9837         this.cells = this.el.select("table.x-date-inner tbody td");
9838         this.textNodes = this.el.query("table.x-date-inner tbody span");
9839
9840         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9841             text: "&#160;",
9842             tooltip: this.monthYearText
9843         });
9844
9845         this.mbtn.on('click', this.showMonthPicker, this);
9846         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9847
9848
9849         var today = (new Date()).dateFormat(this.format);
9850         
9851         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852         if (this.showClear) {
9853             baseTb.add( new Roo.Toolbar.Fill());
9854         }
9855         baseTb.add({
9856             text: String.format(this.todayText, today),
9857             tooltip: String.format(this.todayTip, today),
9858             handler: this.selectToday,
9859             scope: this
9860         });
9861         
9862         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9863             
9864         //});
9865         if (this.showClear) {
9866             
9867             baseTb.add( new Roo.Toolbar.Fill());
9868             baseTb.add({
9869                 text: '&#160;',
9870                 cls: 'x-btn-icon x-btn-clear',
9871                 handler: function() {
9872                     //this.value = '';
9873                     this.fireEvent("select", this, '');
9874                 },
9875                 scope: this
9876             });
9877         }
9878         
9879         
9880         if(Roo.isIE){
9881             this.el.repaint();
9882         }
9883         this.update(this.value);
9884     },
9885
9886     createMonthPicker : function(){
9887         if(!this.monthPicker.dom.firstChild){
9888             var buf = ['<table border="0" cellspacing="0">'];
9889             for(var i = 0; i < 6; i++){
9890                 buf.push(
9891                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9893                     i == 0 ?
9894                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
9895                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9896                 );
9897             }
9898             buf.push(
9899                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9900                     this.okText,
9901                     '</button><button type="button" class="x-date-mp-cancel">',
9902                     this.cancelText,
9903                     '</button></td></tr>',
9904                 '</table>'
9905             );
9906             this.monthPicker.update(buf.join(''));
9907             this.monthPicker.on('click', this.onMonthClick, this);
9908             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9909
9910             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9912
9913             this.mpMonths.each(function(m, a, i){
9914                 i += 1;
9915                 if((i%2) == 0){
9916                     m.dom.xmonth = 5 + Math.round(i * .5);
9917                 }else{
9918                     m.dom.xmonth = Math.round((i-1) * .5);
9919                 }
9920             });
9921         }
9922     },
9923
9924     showMonthPicker : function(){
9925         this.createMonthPicker();
9926         var size = this.el.getSize();
9927         this.monthPicker.setSize(size);
9928         this.monthPicker.child('table').setSize(size);
9929
9930         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931         this.updateMPMonth(this.mpSelMonth);
9932         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933         this.updateMPYear(this.mpSelYear);
9934
9935         this.monthPicker.slideIn('t', {duration:.2});
9936     },
9937
9938     updateMPYear : function(y){
9939         this.mpyear = y;
9940         var ys = this.mpYears.elements;
9941         for(var i = 1; i <= 10; i++){
9942             var td = ys[i-1], y2;
9943             if((i%2) == 0){
9944                 y2 = y + Math.round(i * .5);
9945                 td.firstChild.innerHTML = y2;
9946                 td.xyear = y2;
9947             }else{
9948                 y2 = y - (5-Math.round(i * .5));
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }
9952             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9953         }
9954     },
9955
9956     updateMPMonth : function(sm){
9957         this.mpMonths.each(function(m, a, i){
9958             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9959         });
9960     },
9961
9962     selectMPMonth: function(m){
9963         
9964     },
9965
9966     onMonthClick : function(e, t){
9967         e.stopEvent();
9968         var el = new Roo.Element(t), pn;
9969         if(el.is('button.x-date-mp-cancel')){
9970             this.hideMonthPicker();
9971         }
9972         else if(el.is('button.x-date-mp-ok')){
9973             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-month', 2)){
9977             this.mpMonths.removeClass('x-date-mp-sel');
9978             pn.addClass('x-date-mp-sel');
9979             this.mpSelMonth = pn.dom.xmonth;
9980         }
9981         else if(pn = el.up('td.x-date-mp-year', 2)){
9982             this.mpYears.removeClass('x-date-mp-sel');
9983             pn.addClass('x-date-mp-sel');
9984             this.mpSelYear = pn.dom.xyear;
9985         }
9986         else if(el.is('a.x-date-mp-prev')){
9987             this.updateMPYear(this.mpyear-10);
9988         }
9989         else if(el.is('a.x-date-mp-next')){
9990             this.updateMPYear(this.mpyear+10);
9991         }
9992     },
9993
9994     onMonthDblClick : function(e, t){
9995         e.stopEvent();
9996         var el = new Roo.Element(t), pn;
9997         if(pn = el.up('td.x-date-mp-month', 2)){
9998             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999             this.hideMonthPicker();
10000         }
10001         else if(pn = el.up('td.x-date-mp-year', 2)){
10002             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005     },
10006
10007     hideMonthPicker : function(disableAnim){
10008         if(this.monthPicker){
10009             if(disableAnim === true){
10010                 this.monthPicker.hide();
10011             }else{
10012                 this.monthPicker.slideOut('t', {duration:.2});
10013             }
10014         }
10015     },
10016
10017     // private
10018     showPrevMonth : function(e){
10019         this.update(this.activeDate.add("mo", -1));
10020     },
10021
10022     // private
10023     showNextMonth : function(e){
10024         this.update(this.activeDate.add("mo", 1));
10025     },
10026
10027     // private
10028     showPrevYear : function(){
10029         this.update(this.activeDate.add("y", -1));
10030     },
10031
10032     // private
10033     showNextYear : function(){
10034         this.update(this.activeDate.add("y", 1));
10035     },
10036
10037     // private
10038     handleMouseWheel : function(e){
10039         var delta = e.getWheelDelta();
10040         if(delta > 0){
10041             this.showPrevMonth();
10042             e.stopEvent();
10043         } else if(delta < 0){
10044             this.showNextMonth();
10045             e.stopEvent();
10046         }
10047     },
10048
10049     // private
10050     handleDateClick : function(e, t){
10051         e.stopEvent();
10052         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053             this.setValue(new Date(t.dateValue));
10054             this.fireEvent("select", this, this.value);
10055         }
10056     },
10057
10058     // private
10059     selectToday : function(){
10060         this.setValue(new Date().clearTime());
10061         this.fireEvent("select", this, this.value);
10062     },
10063
10064     // private
10065     update : function(date)
10066     {
10067         var vd = this.activeDate;
10068         this.activeDate = date;
10069         if(vd && this.el){
10070             var t = date.getTime();
10071             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072                 this.cells.removeClass("x-date-selected");
10073                 this.cells.each(function(c){
10074                    if(c.dom.firstChild.dateValue == t){
10075                        c.addClass("x-date-selected");
10076                        setTimeout(function(){
10077                             try{c.dom.firstChild.focus();}catch(e){}
10078                        }, 50);
10079                        return false;
10080                    }
10081                 });
10082                 return;
10083             }
10084         }
10085         
10086         var days = date.getDaysInMonth();
10087         var firstOfMonth = date.getFirstDateOfMonth();
10088         var startingPos = firstOfMonth.getDay()-this.startDay;
10089
10090         if(startingPos <= this.startDay){
10091             startingPos += 7;
10092         }
10093
10094         var pm = date.add("mo", -1);
10095         var prevStart = pm.getDaysInMonth()-startingPos;
10096
10097         var cells = this.cells.elements;
10098         var textEls = this.textNodes;
10099         days += startingPos;
10100
10101         // convert everything to numbers so it's fast
10102         var day = 86400000;
10103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104         var today = new Date().clearTime().getTime();
10105         var sel = date.clearTime().getTime();
10106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108         var ddMatch = this.disabledDatesRE;
10109         var ddText = this.disabledDatesText;
10110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111         var ddaysText = this.disabledDaysText;
10112         var format = this.format;
10113
10114         var setCellClass = function(cal, cell){
10115             cell.title = "";
10116             var t = d.getTime();
10117             cell.firstChild.dateValue = t;
10118             if(t == today){
10119                 cell.className += " x-date-today";
10120                 cell.title = cal.todayText;
10121             }
10122             if(t == sel){
10123                 cell.className += " x-date-selected";
10124                 setTimeout(function(){
10125                     try{cell.firstChild.focus();}catch(e){}
10126                 }, 50);
10127             }
10128             // disabling
10129             if(t < min) {
10130                 cell.className = " x-date-disabled";
10131                 cell.title = cal.minText;
10132                 return;
10133             }
10134             if(t > max) {
10135                 cell.className = " x-date-disabled";
10136                 cell.title = cal.maxText;
10137                 return;
10138             }
10139             if(ddays){
10140                 if(ddays.indexOf(d.getDay()) != -1){
10141                     cell.title = ddaysText;
10142                     cell.className = " x-date-disabled";
10143                 }
10144             }
10145             if(ddMatch && format){
10146                 var fvalue = d.dateFormat(format);
10147                 if(ddMatch.test(fvalue)){
10148                     cell.title = ddText.replace("%0", fvalue);
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152         };
10153
10154         var i = 0;
10155         for(; i < startingPos; i++) {
10156             textEls[i].innerHTML = (++prevStart);
10157             d.setDate(d.getDate()+1);
10158             cells[i].className = "x-date-prevday";
10159             setCellClass(this, cells[i]);
10160         }
10161         for(; i < days; i++){
10162             intDay = i - startingPos + 1;
10163             textEls[i].innerHTML = (intDay);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-active";
10166             setCellClass(this, cells[i]);
10167         }
10168         var extraDays = 0;
10169         for(; i < 42; i++) {
10170              textEls[i].innerHTML = (++extraDays);
10171              d.setDate(d.getDate()+1);
10172              cells[i].className = "x-date-nextday";
10173              setCellClass(this, cells[i]);
10174         }
10175
10176         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177         this.fireEvent('monthchange', this, date);
10178         
10179         if(!this.internalRender){
10180             var main = this.el.dom.firstChild;
10181             var w = main.offsetWidth;
10182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183             Roo.fly(main).setWidth(w);
10184             this.internalRender = true;
10185             // opera does not respect the auto grow header center column
10186             // then, after it gets a width opera refuses to recalculate
10187             // without a second pass
10188             if(Roo.isOpera && !this.secondPass){
10189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190                 this.secondPass = true;
10191                 this.update.defer(10, this, [date]);
10192             }
10193         }
10194         
10195         
10196     }
10197 });        /*
10198  * Based on:
10199  * Ext JS Library 1.1.1
10200  * Copyright(c) 2006-2007, Ext JS, LLC.
10201  *
10202  * Originally Released Under LGPL - original licence link has changed is not relivant.
10203  *
10204  * Fork - LGPL
10205  * <script type="text/javascript">
10206  */
10207 /**
10208  * @class Roo.TabPanel
10209  * @extends Roo.util.Observable
10210  * A lightweight tab container.
10211  * <br><br>
10212  * Usage:
10213  * <pre><code>
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10219
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10223
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10229
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10233
10234 // Disabled tab
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10236 tab4.disable();
10237
10238 jtabs.activate("jtabs-1");
10239  * </code></pre>
10240  * @constructor
10241  * Create a new TabPanel.
10242  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10244  */
10245 Roo.TabPanel = function(container, config){
10246     /**
10247     * The container element for this TabPanel.
10248     * @type Roo.Element
10249     */
10250     this.el = Roo.get(container, true);
10251     if(config){
10252         if(typeof config == "boolean"){
10253             this.tabPosition = config ? "bottom" : "top";
10254         }else{
10255             Roo.apply(this, config);
10256         }
10257     }
10258     if(this.tabPosition == "bottom"){
10259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260         this.el.addClass("x-tabs-bottom");
10261     }
10262     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10265     if(Roo.isIE){
10266         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10267     }
10268     if(this.tabPosition != "bottom"){
10269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270          * @type Roo.Element
10271          */
10272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273         this.el.addClass("x-tabs-top");
10274     }
10275     this.items = [];
10276
10277     this.bodyEl.setStyle("position", "relative");
10278
10279     this.active = null;
10280     this.activateDelegate = this.activate.createDelegate(this);
10281
10282     this.addEvents({
10283         /**
10284          * @event tabchange
10285          * Fires when the active tab changes
10286          * @param {Roo.TabPanel} this
10287          * @param {Roo.TabPanelItem} activePanel The new active tab
10288          */
10289         "tabchange": true,
10290         /**
10291          * @event beforetabchange
10292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293          * @param {Roo.TabPanel} this
10294          * @param {Object} e Set cancel to true on this object to cancel the tab change
10295          * @param {Roo.TabPanelItem} tab The tab being changed to
10296          */
10297         "beforetabchange" : true
10298     });
10299
10300     Roo.EventManager.onWindowResize(this.onResize, this);
10301     this.cpad = this.el.getPadding("lr");
10302     this.hiddenCount = 0;
10303
10304
10305     // toolbar on the tabbar support...
10306     if (this.toolbar) {
10307         var tcfg = this.toolbar;
10308         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10309         this.toolbar = new Roo.Toolbar(tcfg);
10310         if (Roo.isSafari) {
10311             var tbl = tcfg.container.child('table', true);
10312             tbl.setAttribute('width', '100%');
10313         }
10314         
10315     }
10316    
10317
10318
10319     Roo.TabPanel.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10323     /*
10324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10325      */
10326     tabPosition : "top",
10327     /*
10328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10329      */
10330     currentTabWidth : 0,
10331     /*
10332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10333      */
10334     minTabWidth : 40,
10335     /*
10336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     maxTabWidth : 250,
10339     /*
10340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     preferredTabWidth : 175,
10343     /*
10344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10345      */
10346     resizeTabs : false,
10347     /*
10348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10349      */
10350     monitorResize : true,
10351     /*
10352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10353      */
10354     toolbar : false,
10355
10356     /**
10357      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358      * @param {String} id The id of the div to use <b>or create</b>
10359      * @param {String} text The text for the tab
10360      * @param {String} content (optional) Content to put in the TabPanelItem body
10361      * @param {Boolean} closable (optional) True to create a close icon on the tab
10362      * @return {Roo.TabPanelItem} The created TabPanelItem
10363      */
10364     addTab : function(id, text, content, closable){
10365         var item = new Roo.TabPanelItem(this, id, text, closable);
10366         this.addTabItem(item);
10367         if(content){
10368             item.setContent(content);
10369         }
10370         return item;
10371     },
10372
10373     /**
10374      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376      * @return {Roo.TabPanelItem}
10377      */
10378     getTab : function(id){
10379         return this.items[id];
10380     },
10381
10382     /**
10383      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10385      */
10386     hideTab : function(id){
10387         var t = this.items[id];
10388         if(!t.isHidden()){
10389            t.setHidden(true);
10390            this.hiddenCount++;
10391            this.autoSizeTabs();
10392         }
10393     },
10394
10395     /**
10396      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10398      */
10399     unhideTab : function(id){
10400         var t = this.items[id];
10401         if(t.isHidden()){
10402            t.setHidden(false);
10403            this.hiddenCount--;
10404            this.autoSizeTabs();
10405         }
10406     },
10407
10408     /**
10409      * Adds an existing {@link Roo.TabPanelItem}.
10410      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10411      */
10412     addTabItem : function(item){
10413         this.items[item.id] = item;
10414         this.items.push(item);
10415         if(this.resizeTabs){
10416            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417            this.autoSizeTabs();
10418         }else{
10419             item.autoSize();
10420         }
10421     },
10422
10423     /**
10424      * Removes a {@link Roo.TabPanelItem}.
10425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10426      */
10427     removeTab : function(id){
10428         var items = this.items;
10429         var tab = items[id];
10430         if(!tab) { return; }
10431         var index = items.indexOf(tab);
10432         if(this.active == tab && items.length > 1){
10433             var newTab = this.getNextAvailable(index);
10434             if(newTab) {
10435                 newTab.activate();
10436             }
10437         }
10438         this.stripEl.dom.removeChild(tab.pnode.dom);
10439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10441         }
10442         items.splice(index, 1);
10443         delete this.items[tab.id];
10444         tab.fireEvent("close", tab);
10445         tab.purgeListeners();
10446         this.autoSizeTabs();
10447     },
10448
10449     getNextAvailable : function(start){
10450         var items = this.items;
10451         var index = start;
10452         // look for a next tab that will slide over to
10453         // replace the one being removed
10454         while(index < items.length){
10455             var item = items[++index];
10456             if(item && !item.isHidden()){
10457                 return item;
10458             }
10459         }
10460         // if one isn't found select the previous tab (on the left)
10461         index = start;
10462         while(index >= 0){
10463             var item = items[--index];
10464             if(item && !item.isHidden()){
10465                 return item;
10466             }
10467         }
10468         return null;
10469     },
10470
10471     /**
10472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10474      */
10475     disableTab : function(id){
10476         var tab = this.items[id];
10477         if(tab && this.active != tab){
10478             tab.disable();
10479         }
10480     },
10481
10482     /**
10483      * Enables a {@link Roo.TabPanelItem} that is disabled.
10484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10485      */
10486     enableTab : function(id){
10487         var tab = this.items[id];
10488         tab.enable();
10489     },
10490
10491     /**
10492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494      * @return {Roo.TabPanelItem} The TabPanelItem.
10495      */
10496     activate : function(id){
10497         var tab = this.items[id];
10498         if(!tab){
10499             return null;
10500         }
10501         if(tab == this.active || tab.disabled){
10502             return tab;
10503         }
10504         var e = {};
10505         this.fireEvent("beforetabchange", this, e, tab);
10506         if(e.cancel !== true && !tab.disabled){
10507             if(this.active){
10508                 this.active.hide();
10509             }
10510             this.active = this.items[id];
10511             this.active.show();
10512             this.fireEvent("tabchange", this, this.active);
10513         }
10514         return tab;
10515     },
10516
10517     /**
10518      * Gets the active {@link Roo.TabPanelItem}.
10519      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10520      */
10521     getActiveTab : function(){
10522         return this.active;
10523     },
10524
10525     /**
10526      * Updates the tab body element to fit the height of the container element
10527      * for overflow scrolling
10528      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10529      */
10530     syncHeight : function(targetHeight){
10531         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532         var bm = this.bodyEl.getMargins();
10533         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534         this.bodyEl.setHeight(newHeight);
10535         return newHeight;
10536     },
10537
10538     onResize : function(){
10539         if(this.monitorResize){
10540             this.autoSizeTabs();
10541         }
10542     },
10543
10544     /**
10545      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10546      */
10547     beginUpdate : function(){
10548         this.updating = true;
10549     },
10550
10551     /**
10552      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     endUpdate : function(){
10555         this.updating = false;
10556         this.autoSizeTabs();
10557     },
10558
10559     /**
10560      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10561      */
10562     autoSizeTabs : function(){
10563         var count = this.items.length;
10564         var vcount = count - this.hiddenCount;
10565         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567         var availWidth = Math.floor(w / vcount);
10568         var b = this.stripBody;
10569         if(b.getWidth() > w){
10570             var tabs = this.items;
10571             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572             if(availWidth < this.minTabWidth){
10573                 /*if(!this.sleft){    // incomplete scrolling code
10574                     this.createScrollButtons();
10575                 }
10576                 this.showScroll();
10577                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10578             }
10579         }else{
10580             if(this.currentTabWidth < this.preferredTabWidth){
10581                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10582             }
10583         }
10584     },
10585
10586     /**
10587      * Returns the number of tabs in this TabPanel.
10588      * @return {Number}
10589      */
10590      getCount : function(){
10591          return this.items.length;
10592      },
10593
10594     /**
10595      * Resizes all the tabs to the passed width
10596      * @param {Number} The new width
10597      */
10598     setTabWidth : function(width){
10599         this.currentTabWidth = width;
10600         for(var i = 0, len = this.items.length; i < len; i++) {
10601                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10602         }
10603     },
10604
10605     /**
10606      * Destroys this TabPanel
10607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10608      */
10609     destroy : function(removeEl){
10610         Roo.EventManager.removeResizeListener(this.onResize, this);
10611         for(var i = 0, len = this.items.length; i < len; i++){
10612             this.items[i].purgeListeners();
10613         }
10614         if(removeEl === true){
10615             this.el.update("");
10616             this.el.remove();
10617         }
10618     }
10619 });
10620
10621 /**
10622  * @class Roo.TabPanelItem
10623  * @extends Roo.util.Observable
10624  * Represents an individual item (tab plus body) in a TabPanel.
10625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626  * @param {String} id The id of this TabPanelItem
10627  * @param {String} text The text for the tab of this TabPanelItem
10628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10629  */
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10631     /**
10632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633      * @type Roo.TabPanel
10634      */
10635     this.tabPanel = tabPanel;
10636     /**
10637      * The id for this TabPanelItem
10638      * @type String
10639      */
10640     this.id = id;
10641     /** @private */
10642     this.disabled = false;
10643     /** @private */
10644     this.text = text;
10645     /** @private */
10646     this.loaded = false;
10647     this.closable = closable;
10648
10649     /**
10650      * The body element for this TabPanelItem.
10651      * @type Roo.Element
10652      */
10653     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655     this.bodyEl.setStyle("display", "block");
10656     this.bodyEl.setStyle("zoom", "1");
10657     this.hideAction();
10658
10659     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10660     /** @private */
10661     this.el = Roo.get(els.el, true);
10662     this.inner = Roo.get(els.inner, true);
10663     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664     this.pnode = Roo.get(els.el.parentNode, true);
10665     this.el.on("mousedown", this.onTabMouseDown, this);
10666     this.el.on("click", this.onTabClick, this);
10667     /** @private */
10668     if(closable){
10669         var c = Roo.get(els.close, true);
10670         c.dom.title = this.closeText;
10671         c.addClassOnOver("close-over");
10672         c.on("click", this.closeClick, this);
10673      }
10674
10675     this.addEvents({
10676          /**
10677          * @event activate
10678          * Fires when this tab becomes the active tab.
10679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680          * @param {Roo.TabPanelItem} this
10681          */
10682         "activate": true,
10683         /**
10684          * @event beforeclose
10685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686          * @param {Roo.TabPanelItem} this
10687          * @param {Object} e Set cancel to true on this object to cancel the close.
10688          */
10689         "beforeclose": true,
10690         /**
10691          * @event close
10692          * Fires when this tab is closed.
10693          * @param {Roo.TabPanelItem} this
10694          */
10695          "close": true,
10696         /**
10697          * @event deactivate
10698          * Fires when this tab is no longer the active tab.
10699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700          * @param {Roo.TabPanelItem} this
10701          */
10702          "deactivate" : true
10703     });
10704     this.hidden = false;
10705
10706     Roo.TabPanelItem.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710     purgeListeners : function(){
10711        Roo.util.Observable.prototype.purgeListeners.call(this);
10712        this.el.removeAllListeners();
10713     },
10714     /**
10715      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10716      */
10717     show : function(){
10718         this.pnode.addClass("on");
10719         this.showAction();
10720         if(Roo.isOpera){
10721             this.tabPanel.stripWrap.repaint();
10722         }
10723         this.fireEvent("activate", this.tabPanel, this);
10724     },
10725
10726     /**
10727      * Returns true if this tab is the active tab.
10728      * @return {Boolean}
10729      */
10730     isActive : function(){
10731         return this.tabPanel.getActiveTab() == this;
10732     },
10733
10734     /**
10735      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10736      */
10737     hide : function(){
10738         this.pnode.removeClass("on");
10739         this.hideAction();
10740         this.fireEvent("deactivate", this.tabPanel, this);
10741     },
10742
10743     hideAction : function(){
10744         this.bodyEl.hide();
10745         this.bodyEl.setStyle("position", "absolute");
10746         this.bodyEl.setLeft("-20000px");
10747         this.bodyEl.setTop("-20000px");
10748     },
10749
10750     showAction : function(){
10751         this.bodyEl.setStyle("position", "relative");
10752         this.bodyEl.setTop("");
10753         this.bodyEl.setLeft("");
10754         this.bodyEl.show();
10755     },
10756
10757     /**
10758      * Set the tooltip for the tab.
10759      * @param {String} tooltip The tab's tooltip
10760      */
10761     setTooltip : function(text){
10762         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763             this.textEl.dom.qtip = text;
10764             this.textEl.dom.removeAttribute('title');
10765         }else{
10766             this.textEl.dom.title = text;
10767         }
10768     },
10769
10770     onTabClick : function(e){
10771         e.preventDefault();
10772         this.tabPanel.activate(this.id);
10773     },
10774
10775     onTabMouseDown : function(e){
10776         e.preventDefault();
10777         this.tabPanel.activate(this.id);
10778     },
10779
10780     getWidth : function(){
10781         return this.inner.getWidth();
10782     },
10783
10784     setWidth : function(width){
10785         var iwidth = width - this.pnode.getPadding("lr");
10786         this.inner.setWidth(iwidth);
10787         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788         this.pnode.setWidth(width);
10789     },
10790
10791     /**
10792      * Show or hide the tab
10793      * @param {Boolean} hidden True to hide or false to show.
10794      */
10795     setHidden : function(hidden){
10796         this.hidden = hidden;
10797         this.pnode.setStyle("display", hidden ? "none" : "");
10798     },
10799
10800     /**
10801      * Returns true if this tab is "hidden"
10802      * @return {Boolean}
10803      */
10804     isHidden : function(){
10805         return this.hidden;
10806     },
10807
10808     /**
10809      * Returns the text for this tab
10810      * @return {String}
10811      */
10812     getText : function(){
10813         return this.text;
10814     },
10815
10816     autoSize : function(){
10817         //this.el.beginMeasure();
10818         this.textEl.setWidth(1);
10819         /*
10820          *  #2804 [new] Tabs in Roojs
10821          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10822          */
10823         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824         //this.el.endMeasure();
10825     },
10826
10827     /**
10828      * Sets the text for the tab (Note: this also sets the tooltip text)
10829      * @param {String} text The tab's text and tooltip
10830      */
10831     setText : function(text){
10832         this.text = text;
10833         this.textEl.update(text);
10834         this.setTooltip(text);
10835         if(!this.tabPanel.resizeTabs){
10836             this.autoSize();
10837         }
10838     },
10839     /**
10840      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10841      */
10842     activate : function(){
10843         this.tabPanel.activate(this.id);
10844     },
10845
10846     /**
10847      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10848      */
10849     disable : function(){
10850         if(this.tabPanel.active != this){
10851             this.disabled = true;
10852             this.pnode.addClass("disabled");
10853         }
10854     },
10855
10856     /**
10857      * Enables this TabPanelItem if it was previously disabled.
10858      */
10859     enable : function(){
10860         this.disabled = false;
10861         this.pnode.removeClass("disabled");
10862     },
10863
10864     /**
10865      * Sets the content for this TabPanelItem.
10866      * @param {String} content The content
10867      * @param {Boolean} loadScripts true to look for and load scripts
10868      */
10869     setContent : function(content, loadScripts){
10870         this.bodyEl.update(content, loadScripts);
10871     },
10872
10873     /**
10874      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875      * @return {Roo.UpdateManager} The UpdateManager
10876      */
10877     getUpdateManager : function(){
10878         return this.bodyEl.getUpdateManager();
10879     },
10880
10881     /**
10882      * Set a URL to be used to load the content for this TabPanelItem.
10883      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
10885      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     setUrl : function(url, params, loadOnce){
10889         if(this.refreshDelegate){
10890             this.un('activate', this.refreshDelegate);
10891         }
10892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893         this.on("activate", this.refreshDelegate);
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /** @private */
10898     _handleRefresh : function(url, params, loadOnce){
10899         if(!loadOnce || !this.loaded){
10900             var updater = this.bodyEl.getUpdateManager();
10901             updater.update(url, params, this._setLoaded.createDelegate(this));
10902         }
10903     },
10904
10905     /**
10906      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907      *   Will fail silently if the setUrl method has not been called.
10908      *   This does not activate the panel, just updates its content.
10909      */
10910     refresh : function(){
10911         if(this.refreshDelegate){
10912            this.loaded = false;
10913            this.refreshDelegate();
10914         }
10915     },
10916
10917     /** @private */
10918     _setLoaded : function(){
10919         this.loaded = true;
10920     },
10921
10922     /** @private */
10923     closeClick : function(e){
10924         var o = {};
10925         e.stopEvent();
10926         this.fireEvent("beforeclose", this, o);
10927         if(o.cancel !== true){
10928             this.tabPanel.removeTab(this.id);
10929         }
10930     },
10931     /**
10932      * The text displayed in the tooltip for the close icon.
10933      * @type String
10934      */
10935     closeText : "Close this tab"
10936 });
10937
10938 /** @private */
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940     var strip = document.createElement("div");
10941     strip.className = "x-tabs-wrap";
10942     container.appendChild(strip);
10943     return strip;
10944 };
10945 /** @private */
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947     // div wrapper for retard IE
10948     // returns the "tr" element.
10949     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952     return strip.firstChild.firstChild.firstChild.firstChild;
10953 };
10954 /** @private */
10955 Roo.TabPanel.prototype.createBody = function(container){
10956     var body = document.createElement("div");
10957     Roo.id(body, "tab-body");
10958     Roo.fly(body).addClass("x-tabs-body");
10959     container.appendChild(body);
10960     return body;
10961 };
10962 /** @private */
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964     var body = Roo.getDom(id);
10965     if(!body){
10966         body = document.createElement("div");
10967         body.id = id;
10968     }
10969     Roo.fly(body).addClass("x-tabs-item-body");
10970     bodyEl.insertBefore(body, bodyEl.firstChild);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975     var td = document.createElement("td");
10976     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977     //stripEl.appendChild(td);
10978     if(closable){
10979         td.className = "x-tabs-closable";
10980         if(!this.closeTpl){
10981             this.closeTpl = new Roo.Template(
10982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10985             );
10986         }
10987         var el = this.closeTpl.overwrite(td, {"text": text});
10988         var close = el.getElementsByTagName("div")[0];
10989         var inner = el.getElementsByTagName("em")[0];
10990         return {"el": el, "close": close, "inner": inner};
10991     } else {
10992         if(!this.tabTpl){
10993             this.tabTpl = new Roo.Template(
10994                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10996             );
10997         }
10998         var el = this.tabTpl.overwrite(td, {"text": text});
10999         var inner = el.getElementsByTagName("em")[0];
11000         return {"el": el, "inner": inner};
11001     }
11002 };/*
11003  * Based on:
11004  * Ext JS Library 1.1.1
11005  * Copyright(c) 2006-2007, Ext JS, LLC.
11006  *
11007  * Originally Released Under LGPL - original licence link has changed is not relivant.
11008  *
11009  * Fork - LGPL
11010  * <script type="text/javascript">
11011  */
11012
11013 /**
11014  * @class Roo.Button
11015  * @extends Roo.util.Observable
11016  * Simple Button class
11017  * @cfg {String} text The button text
11018  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021  * @cfg {Object} scope The scope of the handler
11022  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028    applies if enableToggle = true)
11029  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11032  * @constructor
11033  * Create a new button
11034  * @param {Object} config The config object
11035  */
11036 Roo.Button = function(renderTo, config)
11037 {
11038     if (!config) {
11039         config = renderTo;
11040         renderTo = config.renderTo || false;
11041     }
11042     
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046              * @event click
11047              * Fires when this button is clicked
11048              * @param {Button} this
11049              * @param {EventObject} e The click event
11050              */
11051             "click" : true,
11052         /**
11053              * @event toggle
11054              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055              * @param {Button} this
11056              * @param {Boolean} pressed
11057              */
11058             "toggle" : true,
11059         /**
11060              * @event mouseover
11061              * Fires when the mouse hovers over the button
11062              * @param {Button} this
11063              * @param {Event} e The event object
11064              */
11065         'mouseover' : true,
11066         /**
11067              * @event mouseout
11068              * Fires when the mouse exits the button
11069              * @param {Button} this
11070              * @param {Event} e The event object
11071              */
11072         'mouseout': true,
11073          /**
11074              * @event render
11075              * Fires when the button is rendered
11076              * @param {Button} this
11077              */
11078         'render': true
11079     });
11080     if(this.menu){
11081         this.menu = Roo.menu.MenuMgr.get(this.menu);
11082     }
11083     // register listeners first!!  - so render can be captured..
11084     Roo.util.Observable.call(this);
11085     if(renderTo){
11086         this.render(renderTo);
11087     }
11088     
11089   
11090 };
11091
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11093     /**
11094      * 
11095      */
11096     
11097     /**
11098      * Read-only. True if this button is hidden
11099      * @type Boolean
11100      */
11101     hidden : false,
11102     /**
11103      * Read-only. True if this button is disabled
11104      * @type Boolean
11105      */
11106     disabled : false,
11107     /**
11108      * Read-only. True if this button is pressed (only if enableToggle = true)
11109      * @type Boolean
11110      */
11111     pressed : false,
11112
11113     /**
11114      * @cfg {Number} tabIndex 
11115      * The DOM tabIndex for this button (defaults to undefined)
11116      */
11117     tabIndex : undefined,
11118
11119     /**
11120      * @cfg {Boolean} enableToggle
11121      * True to enable pressed/not pressed toggling (defaults to false)
11122      */
11123     enableToggle: false,
11124     /**
11125      * @cfg {Mixed} menu
11126      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11127      */
11128     menu : undefined,
11129     /**
11130      * @cfg {String} menuAlign
11131      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11132      */
11133     menuAlign : "tl-bl?",
11134
11135     /**
11136      * @cfg {String} iconCls
11137      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11138      */
11139     iconCls : undefined,
11140     /**
11141      * @cfg {String} type
11142      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11143      */
11144     type : 'button',
11145
11146     // private
11147     menuClassTarget: 'tr',
11148
11149     /**
11150      * @cfg {String} clickEvent
11151      * The type of event to map to the button's event handler (defaults to 'click')
11152      */
11153     clickEvent : 'click',
11154
11155     /**
11156      * @cfg {Boolean} handleMouseEvents
11157      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11158      */
11159     handleMouseEvents : true,
11160
11161     /**
11162      * @cfg {String} tooltipType
11163      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11164      */
11165     tooltipType : 'qtip',
11166
11167     /**
11168      * @cfg {String} cls
11169      * A CSS class to apply to the button's main element.
11170      */
11171     
11172     /**
11173      * @cfg {Roo.Template} template (Optional)
11174      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176      * require code modifications if required elements (e.g. a button) aren't present.
11177      */
11178
11179     // private
11180     render : function(renderTo){
11181         var btn;
11182         if(this.hideParent){
11183             this.parentEl = Roo.get(renderTo);
11184         }
11185         if(!this.dhconfig){
11186             if(!this.template){
11187                 if(!Roo.Button.buttonTemplate){
11188                     // hideous table template
11189                     Roo.Button.buttonTemplate = new Roo.Template(
11190                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11192                         "</tr></tbody></table>");
11193                 }
11194                 this.template = Roo.Button.buttonTemplate;
11195             }
11196             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11197             var btnEl = btn.child("button:first");
11198             btnEl.on('focus', this.onFocus, this);
11199             btnEl.on('blur', this.onBlur, this);
11200             if(this.cls){
11201                 btn.addClass(this.cls);
11202             }
11203             if(this.icon){
11204                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11205             }
11206             if(this.iconCls){
11207                 btnEl.addClass(this.iconCls);
11208                 if(!this.cls){
11209                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11210                 }
11211             }
11212             if(this.tabIndex !== undefined){
11213                 btnEl.dom.tabIndex = this.tabIndex;
11214             }
11215             if(this.tooltip){
11216                 if(typeof this.tooltip == 'object'){
11217                     Roo.QuickTips.tips(Roo.apply({
11218                           target: btnEl.id
11219                     }, this.tooltip));
11220                 } else {
11221                     btnEl.dom[this.tooltipType] = this.tooltip;
11222                 }
11223             }
11224         }else{
11225             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11226         }
11227         this.el = btn;
11228         if(this.id){
11229             this.el.dom.id = this.el.id = this.id;
11230         }
11231         if(this.menu){
11232             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233             this.menu.on("show", this.onMenuShow, this);
11234             this.menu.on("hide", this.onMenuHide, this);
11235         }
11236         btn.addClass("x-btn");
11237         if(Roo.isIE && !Roo.isIE7){
11238             this.autoWidth.defer(1, this);
11239         }else{
11240             this.autoWidth();
11241         }
11242         if(this.handleMouseEvents){
11243             btn.on("mouseover", this.onMouseOver, this);
11244             btn.on("mouseout", this.onMouseOut, this);
11245             btn.on("mousedown", this.onMouseDown, this);
11246         }
11247         btn.on(this.clickEvent, this.onClick, this);
11248         //btn.on("mouseup", this.onMouseUp, this);
11249         if(this.hidden){
11250             this.hide();
11251         }
11252         if(this.disabled){
11253             this.disable();
11254         }
11255         Roo.ButtonToggleMgr.register(this);
11256         if(this.pressed){
11257             this.el.addClass("x-btn-pressed");
11258         }
11259         if(this.repeat){
11260             var repeater = new Roo.util.ClickRepeater(btn,
11261                 typeof this.repeat == "object" ? this.repeat : {}
11262             );
11263             repeater.on("click", this.onClick,  this);
11264         }
11265         
11266         this.fireEvent('render', this);
11267         
11268     },
11269     /**
11270      * Returns the button's underlying element
11271      * @return {Roo.Element} The element
11272      */
11273     getEl : function(){
11274         return this.el;  
11275     },
11276     
11277     /**
11278      * Destroys this Button and removes any listeners.
11279      */
11280     destroy : function(){
11281         Roo.ButtonToggleMgr.unregister(this);
11282         this.el.removeAllListeners();
11283         this.purgeListeners();
11284         this.el.remove();
11285     },
11286
11287     // private
11288     autoWidth : function(){
11289         if(this.el){
11290             this.el.setWidth("auto");
11291             if(Roo.isIE7 && Roo.isStrict){
11292                 var ib = this.el.child('button');
11293                 if(ib && ib.getWidth() > 20){
11294                     ib.clip();
11295                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11296                 }
11297             }
11298             if(this.minWidth){
11299                 if(this.hidden){
11300                     this.el.beginMeasure();
11301                 }
11302                 if(this.el.getWidth() < this.minWidth){
11303                     this.el.setWidth(this.minWidth);
11304                 }
11305                 if(this.hidden){
11306                     this.el.endMeasure();
11307                 }
11308             }
11309         }
11310     },
11311
11312     /**
11313      * Assigns this button's click handler
11314      * @param {Function} handler The function to call when the button is clicked
11315      * @param {Object} scope (optional) Scope for the function passed in
11316      */
11317     setHandler : function(handler, scope){
11318         this.handler = handler;
11319         this.scope = scope;  
11320     },
11321     
11322     /**
11323      * Sets this button's text
11324      * @param {String} text The button text
11325      */
11326     setText : function(text){
11327         this.text = text;
11328         if(this.el){
11329             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11330         }
11331         this.autoWidth();
11332     },
11333     
11334     /**
11335      * Gets the text for this button
11336      * @return {String} The button text
11337      */
11338     getText : function(){
11339         return this.text;  
11340     },
11341     
11342     /**
11343      * Show this button
11344      */
11345     show: function(){
11346         this.hidden = false;
11347         if(this.el){
11348             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11349         }
11350     },
11351     
11352     /**
11353      * Hide this button
11354      */
11355     hide: function(){
11356         this.hidden = true;
11357         if(this.el){
11358             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11359         }
11360     },
11361     
11362     /**
11363      * Convenience function for boolean show/hide
11364      * @param {Boolean} visible True to show, false to hide
11365      */
11366     setVisible: function(visible){
11367         if(visible) {
11368             this.show();
11369         }else{
11370             this.hide();
11371         }
11372     },
11373     
11374     /**
11375      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376      * @param {Boolean} state (optional) Force a particular state
11377      */
11378     toggle : function(state){
11379         state = state === undefined ? !this.pressed : state;
11380         if(state != this.pressed){
11381             if(state){
11382                 this.el.addClass("x-btn-pressed");
11383                 this.pressed = true;
11384                 this.fireEvent("toggle", this, true);
11385             }else{
11386                 this.el.removeClass("x-btn-pressed");
11387                 this.pressed = false;
11388                 this.fireEvent("toggle", this, false);
11389             }
11390             if(this.toggleHandler){
11391                 this.toggleHandler.call(this.scope || this, this, state);
11392             }
11393         }
11394     },
11395     
11396     /**
11397      * Focus the button
11398      */
11399     focus : function(){
11400         this.el.child('button:first').focus();
11401     },
11402     
11403     /**
11404      * Disable this button
11405      */
11406     disable : function(){
11407         if(this.el){
11408             this.el.addClass("x-btn-disabled");
11409         }
11410         this.disabled = true;
11411     },
11412     
11413     /**
11414      * Enable this button
11415      */
11416     enable : function(){
11417         if(this.el){
11418             this.el.removeClass("x-btn-disabled");
11419         }
11420         this.disabled = false;
11421     },
11422
11423     /**
11424      * Convenience function for boolean enable/disable
11425      * @param {Boolean} enabled True to enable, false to disable
11426      */
11427     setDisabled : function(v){
11428         this[v !== true ? "enable" : "disable"]();
11429     },
11430
11431     // private
11432     onClick : function(e){
11433         if(e){
11434             e.preventDefault();
11435         }
11436         if(e.button != 0){
11437             return;
11438         }
11439         if(!this.disabled){
11440             if(this.enableToggle){
11441                 this.toggle();
11442             }
11443             if(this.menu && !this.menu.isVisible()){
11444                 this.menu.show(this.el, this.menuAlign);
11445             }
11446             this.fireEvent("click", this, e);
11447             if(this.handler){
11448                 this.el.removeClass("x-btn-over");
11449                 this.handler.call(this.scope || this, this, e);
11450             }
11451         }
11452     },
11453     // private
11454     onMouseOver : function(e){
11455         if(!this.disabled){
11456             this.el.addClass("x-btn-over");
11457             this.fireEvent('mouseover', this, e);
11458         }
11459     },
11460     // private
11461     onMouseOut : function(e){
11462         if(!e.within(this.el,  true)){
11463             this.el.removeClass("x-btn-over");
11464             this.fireEvent('mouseout', this, e);
11465         }
11466     },
11467     // private
11468     onFocus : function(e){
11469         if(!this.disabled){
11470             this.el.addClass("x-btn-focus");
11471         }
11472     },
11473     // private
11474     onBlur : function(e){
11475         this.el.removeClass("x-btn-focus");
11476     },
11477     // private
11478     onMouseDown : function(e){
11479         if(!this.disabled && e.button == 0){
11480             this.el.addClass("x-btn-click");
11481             Roo.get(document).on('mouseup', this.onMouseUp, this);
11482         }
11483     },
11484     // private
11485     onMouseUp : function(e){
11486         if(e.button == 0){
11487             this.el.removeClass("x-btn-click");
11488             Roo.get(document).un('mouseup', this.onMouseUp, this);
11489         }
11490     },
11491     // private
11492     onMenuShow : function(e){
11493         this.el.addClass("x-btn-menu-active");
11494     },
11495     // private
11496     onMenuHide : function(e){
11497         this.el.removeClass("x-btn-menu-active");
11498     }   
11499 });
11500
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11503    var groups = {};
11504    
11505    function toggleGroup(btn, state){
11506        if(state){
11507            var g = groups[btn.toggleGroup];
11508            for(var i = 0, l = g.length; i < l; i++){
11509                if(g[i] != btn){
11510                    g[i].toggle(false);
11511                }
11512            }
11513        }
11514    }
11515    
11516    return {
11517        register : function(btn){
11518            if(!btn.toggleGroup){
11519                return;
11520            }
11521            var g = groups[btn.toggleGroup];
11522            if(!g){
11523                g = groups[btn.toggleGroup] = [];
11524            }
11525            g.push(btn);
11526            btn.on("toggle", toggleGroup);
11527        },
11528        
11529        unregister : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(g){
11535                g.remove(btn);
11536                btn.un("toggle", toggleGroup);
11537            }
11538        }
11539    };
11540 }();/*
11541  * Based on:
11542  * Ext JS Library 1.1.1
11543  * Copyright(c) 2006-2007, Ext JS, LLC.
11544  *
11545  * Originally Released Under LGPL - original licence link has changed is not relivant.
11546  *
11547  * Fork - LGPL
11548  * <script type="text/javascript">
11549  */
11550  
11551 /**
11552  * @class Roo.SplitButton
11553  * @extends Roo.Button
11554  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11556  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558  * @cfg {String} arrowTooltip The title attribute of the arrow
11559  * @constructor
11560  * Create a new menu button
11561  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562  * @param {Object} config The config object
11563  */
11564 Roo.SplitButton = function(renderTo, config){
11565     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11566     /**
11567      * @event arrowclick
11568      * Fires when this button's arrow is clicked
11569      * @param {SplitButton} this
11570      * @param {EventObject} e The click event
11571      */
11572     this.addEvents({"arrowclick":true});
11573 };
11574
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576     render : function(renderTo){
11577         // this is one sweet looking template!
11578         var tpl = new Roo.Template(
11579             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
11582             "</tbody></table></td><td>",
11583             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
11585             "</tbody></table></td></tr></table>"
11586         );
11587         var btn = tpl.append(renderTo, [this.text, this.type], true);
11588         var btnEl = btn.child("button");
11589         if(this.cls){
11590             btn.addClass(this.cls);
11591         }
11592         if(this.icon){
11593             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11594         }
11595         if(this.iconCls){
11596             btnEl.addClass(this.iconCls);
11597             if(!this.cls){
11598                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11599             }
11600         }
11601         this.el = btn;
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606             btn.on("mouseup", this.onMouseUp, this);
11607         }
11608         btn.on(this.clickEvent, this.onClick, this);
11609         if(this.tooltip){
11610             if(typeof this.tooltip == 'object'){
11611                 Roo.QuickTips.tips(Roo.apply({
11612                       target: btnEl.id
11613                 }, this.tooltip));
11614             } else {
11615                 btnEl.dom[this.tooltipType] = this.tooltip;
11616             }
11617         }
11618         if(this.arrowTooltip){
11619             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11620         }
11621         if(this.hidden){
11622             this.hide();
11623         }
11624         if(this.disabled){
11625             this.disable();
11626         }
11627         if(this.pressed){
11628             this.el.addClass("x-btn-pressed");
11629         }
11630         if(Roo.isIE && !Roo.isIE7){
11631             this.autoWidth.defer(1, this);
11632         }else{
11633             this.autoWidth();
11634         }
11635         if(this.menu){
11636             this.menu.on("show", this.onMenuShow, this);
11637             this.menu.on("hide", this.onMenuHide, this);
11638         }
11639         this.fireEvent('render', this);
11640     },
11641
11642     // private
11643     autoWidth : function(){
11644         if(this.el){
11645             var tbl = this.el.child("table:first");
11646             var tbl2 = this.el.child("table:last");
11647             this.el.setWidth("auto");
11648             tbl.setWidth("auto");
11649             if(Roo.isIE7 && Roo.isStrict){
11650                 var ib = this.el.child('button:first');
11651                 if(ib && ib.getWidth() > 20){
11652                     ib.clip();
11653                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11654                 }
11655             }
11656             if(this.minWidth){
11657                 if(this.hidden){
11658                     this.el.beginMeasure();
11659                 }
11660                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11662                 }
11663                 if(this.hidden){
11664                     this.el.endMeasure();
11665                 }
11666             }
11667             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11668         } 
11669     },
11670     /**
11671      * Sets this button's click handler
11672      * @param {Function} handler The function to call when the button is clicked
11673      * @param {Object} scope (optional) Scope for the function passed above
11674      */
11675     setHandler : function(handler, scope){
11676         this.handler = handler;
11677         this.scope = scope;  
11678     },
11679     
11680     /**
11681      * Sets this button's arrow click handler
11682      * @param {Function} handler The function to call when the arrow is clicked
11683      * @param {Object} scope (optional) Scope for the function passed above
11684      */
11685     setArrowHandler : function(handler, scope){
11686         this.arrowHandler = handler;
11687         this.scope = scope;  
11688     },
11689     
11690     /**
11691      * Focus the button
11692      */
11693     focus : function(){
11694         if(this.el){
11695             this.el.child("button:first").focus();
11696         }
11697     },
11698
11699     // private
11700     onClick : function(e){
11701         e.preventDefault();
11702         if(!this.disabled){
11703             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704                 if(this.menu && !this.menu.isVisible()){
11705                     this.menu.show(this.el, this.menuAlign);
11706                 }
11707                 this.fireEvent("arrowclick", this, e);
11708                 if(this.arrowHandler){
11709                     this.arrowHandler.call(this.scope || this, this, e);
11710                 }
11711             }else{
11712                 this.fireEvent("click", this, e);
11713                 if(this.handler){
11714                     this.handler.call(this.scope || this, this, e);
11715                 }
11716             }
11717         }
11718     },
11719     // private
11720     onMouseDown : function(e){
11721         if(!this.disabled){
11722             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11723         }
11724     },
11725     // private
11726     onMouseUp : function(e){
11727         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11728     }   
11729 });
11730
11731
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11734  * Based on:
11735  * Ext JS Library 1.1.1
11736  * Copyright(c) 2006-2007, Ext JS, LLC.
11737  *
11738  * Originally Released Under LGPL - original licence link has changed is not relivant.
11739  *
11740  * Fork - LGPL
11741  * <script type="text/javascript">
11742  */
11743
11744 /**
11745  * @class Roo.Toolbar
11746  * Basic Toolbar class.
11747  * @constructor
11748  * Creates a new Toolbar
11749  * @param {Object} container The config object
11750  */ 
11751 Roo.Toolbar = function(container, buttons, config)
11752 {
11753     /// old consturctor format still supported..
11754     if(container instanceof Array){ // omit the container for later rendering
11755         buttons = container;
11756         config = buttons;
11757         container = null;
11758     }
11759     if (typeof(container) == 'object' && container.xtype) {
11760         config = container;
11761         container = config.container;
11762         buttons = config.buttons || []; // not really - use items!!
11763     }
11764     var xitems = [];
11765     if (config && config.items) {
11766         xitems = config.items;
11767         delete config.items;
11768     }
11769     Roo.apply(this, config);
11770     this.buttons = buttons;
11771     
11772     if(container){
11773         this.render(container);
11774     }
11775     this.xitems = xitems;
11776     Roo.each(xitems, function(b) {
11777         this.add(b);
11778     }, this);
11779     
11780 };
11781
11782 Roo.Toolbar.prototype = {
11783     /**
11784      * @cfg {Array} items
11785      * array of button configs or elements to add (will be converted to a MixedCollection)
11786      */
11787     
11788     /**
11789      * @cfg {String/HTMLElement/Element} container
11790      * The id or element that will contain the toolbar
11791      */
11792     // private
11793     render : function(ct){
11794         this.el = Roo.get(ct);
11795         if(this.cls){
11796             this.el.addClass(this.cls);
11797         }
11798         // using a table allows for vertical alignment
11799         // 100% width is needed by Safari...
11800         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801         this.tr = this.el.child("tr", true);
11802         var autoId = 0;
11803         this.items = new Roo.util.MixedCollection(false, function(o){
11804             return o.id || ("item" + (++autoId));
11805         });
11806         if(this.buttons){
11807             this.add.apply(this, this.buttons);
11808             delete this.buttons;
11809         }
11810     },
11811
11812     /**
11813      * Adds element(s) to the toolbar -- this function takes a variable number of 
11814      * arguments of mixed type and adds them to the toolbar.
11815      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11816      * <ul>
11817      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11826      * </ul>
11827      * @param {Mixed} arg2
11828      * @param {Mixed} etc.
11829      */
11830     add : function(){
11831         var a = arguments, l = a.length;
11832         for(var i = 0; i < l; i++){
11833             this._add(a[i]);
11834         }
11835     },
11836     // private..
11837     _add : function(el) {
11838         
11839         if (el.xtype) {
11840             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11841         }
11842         
11843         if (el.applyTo){ // some kind of form field
11844             return this.addField(el);
11845         } 
11846         if (el.render){ // some kind of Toolbar.Item
11847             return this.addItem(el);
11848         }
11849         if (typeof el == "string"){ // string
11850             if(el == "separator" || el == "-"){
11851                 return this.addSeparator();
11852             }
11853             if (el == " "){
11854                 return this.addSpacer();
11855             }
11856             if(el == "->"){
11857                 return this.addFill();
11858             }
11859             return this.addText(el);
11860             
11861         }
11862         if(el.tagName){ // element
11863             return this.addElement(el);
11864         }
11865         if(typeof el == "object"){ // must be button config?
11866             return this.addButton(el);
11867         }
11868         // and now what?!?!
11869         return false;
11870         
11871     },
11872     
11873     /**
11874      * Add an Xtype element
11875      * @param {Object} xtype Xtype Object
11876      * @return {Object} created Object
11877      */
11878     addxtype : function(e){
11879         return this.add(e);  
11880     },
11881     
11882     /**
11883      * Returns the Element for this toolbar.
11884      * @return {Roo.Element}
11885      */
11886     getEl : function(){
11887         return this.el;  
11888     },
11889     
11890     /**
11891      * Adds a separator
11892      * @return {Roo.Toolbar.Item} The separator item
11893      */
11894     addSeparator : function(){
11895         return this.addItem(new Roo.Toolbar.Separator());
11896     },
11897
11898     /**
11899      * Adds a spacer element
11900      * @return {Roo.Toolbar.Spacer} The spacer item
11901      */
11902     addSpacer : function(){
11903         return this.addItem(new Roo.Toolbar.Spacer());
11904     },
11905
11906     /**
11907      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908      * @return {Roo.Toolbar.Fill} The fill item
11909      */
11910     addFill : function(){
11911         return this.addItem(new Roo.Toolbar.Fill());
11912     },
11913
11914     /**
11915      * Adds any standard HTML element to the toolbar
11916      * @param {String/HTMLElement/Element} el The element or id of the element to add
11917      * @return {Roo.Toolbar.Item} The element's item
11918      */
11919     addElement : function(el){
11920         return this.addItem(new Roo.Toolbar.Item(el));
11921     },
11922     /**
11923      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924      * @type Roo.util.MixedCollection  
11925      */
11926     items : false,
11927      
11928     /**
11929      * Adds any Toolbar.Item or subclass
11930      * @param {Roo.Toolbar.Item} item
11931      * @return {Roo.Toolbar.Item} The item
11932      */
11933     addItem : function(item){
11934         var td = this.nextBlock();
11935         item.render(td);
11936         this.items.add(item);
11937         return item;
11938     },
11939     
11940     /**
11941      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942      * @param {Object/Array} config A button config or array of configs
11943      * @return {Roo.Toolbar.Button/Array}
11944      */
11945     addButton : function(config){
11946         if(config instanceof Array){
11947             var buttons = [];
11948             for(var i = 0, len = config.length; i < len; i++) {
11949                 buttons.push(this.addButton(config[i]));
11950             }
11951             return buttons;
11952         }
11953         var b = config;
11954         if(!(config instanceof Roo.Toolbar.Button)){
11955             b = config.split ?
11956                 new Roo.Toolbar.SplitButton(config) :
11957                 new Roo.Toolbar.Button(config);
11958         }
11959         var td = this.nextBlock();
11960         b.render(td);
11961         this.items.add(b);
11962         return b;
11963     },
11964     
11965     /**
11966      * Adds text to the toolbar
11967      * @param {String} text The text to add
11968      * @return {Roo.Toolbar.Item} The element's item
11969      */
11970     addText : function(text){
11971         return this.addItem(new Roo.Toolbar.TextItem(text));
11972     },
11973     
11974     /**
11975      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976      * @param {Number} index The index where the item is to be inserted
11977      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978      * @return {Roo.Toolbar.Button/Item}
11979      */
11980     insertButton : function(index, item){
11981         if(item instanceof Array){
11982             var buttons = [];
11983             for(var i = 0, len = item.length; i < len; i++) {
11984                buttons.push(this.insertButton(index + i, item[i]));
11985             }
11986             return buttons;
11987         }
11988         if (!(item instanceof Roo.Toolbar.Button)){
11989            item = new Roo.Toolbar.Button(item);
11990         }
11991         var td = document.createElement("td");
11992         this.tr.insertBefore(td, this.tr.childNodes[index]);
11993         item.render(td);
11994         this.items.insert(index, item);
11995         return item;
11996     },
11997     
11998     /**
11999      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000      * @param {Object} config
12001      * @return {Roo.Toolbar.Item} The element's item
12002      */
12003     addDom : function(config, returnEl){
12004         var td = this.nextBlock();
12005         Roo.DomHelper.overwrite(td, config);
12006         var ti = new Roo.Toolbar.Item(td.firstChild);
12007         ti.render(td);
12008         this.items.add(ti);
12009         return ti;
12010     },
12011
12012     /**
12013      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014      * @type Roo.util.MixedCollection  
12015      */
12016     fields : false,
12017     
12018     /**
12019      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020      * Note: the field should not have been rendered yet. For a field that has already been
12021      * rendered, use {@link #addElement}.
12022      * @param {Roo.form.Field} field
12023      * @return {Roo.ToolbarItem}
12024      */
12025      
12026       
12027     addField : function(field) {
12028         if (!this.fields) {
12029             var autoId = 0;
12030             this.fields = new Roo.util.MixedCollection(false, function(o){
12031                 return o.id || ("item" + (++autoId));
12032             });
12033
12034         }
12035         
12036         var td = this.nextBlock();
12037         field.render(td);
12038         var ti = new Roo.Toolbar.Item(td.firstChild);
12039         ti.render(td);
12040         this.items.add(ti);
12041         this.fields.add(field);
12042         return ti;
12043     },
12044     /**
12045      * Hide the toolbar
12046      * @method hide
12047      */
12048      
12049       
12050     hide : function()
12051     {
12052         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053         this.el.child('div').hide();
12054     },
12055     /**
12056      * Show the toolbar
12057      * @method show
12058      */
12059     show : function()
12060     {
12061         this.el.child('div').show();
12062     },
12063       
12064     // private
12065     nextBlock : function(){
12066         var td = document.createElement("td");
12067         this.tr.appendChild(td);
12068         return td;
12069     },
12070
12071     // private
12072     destroy : function(){
12073         if(this.items){ // rendered?
12074             Roo.destroy.apply(Roo, this.items.items);
12075         }
12076         if(this.fields){ // rendered?
12077             Roo.destroy.apply(Roo, this.fields.items);
12078         }
12079         Roo.Element.uncache(this.el, this.tr);
12080     }
12081 };
12082
12083 /**
12084  * @class Roo.Toolbar.Item
12085  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12086  * @constructor
12087  * Creates a new Item
12088  * @param {HTMLElement} el 
12089  */
12090 Roo.Toolbar.Item = function(el){
12091     this.el = Roo.getDom(el);
12092     this.id = Roo.id(this.el);
12093     this.hidden = false;
12094     
12095     this.addEvents({
12096          /**
12097              * @event render
12098              * Fires when the button is rendered
12099              * @param {Button} this
12100              */
12101         'render': true
12102     });
12103 };
12104 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12105 //Roo.Toolbar.Item.prototype = {
12106     
12107     /**
12108      * Get this item's HTML Element
12109      * @return {HTMLElement}
12110      */
12111     getEl : function(){
12112        return this.el;  
12113     },
12114
12115     // private
12116 //    render : function(td){
12117 //        this.td = td;
12118 //        td.appendChild(this.el);
12119 //        
12120 //        this.fireEvent('render', this);
12121 //    },
12122     render : function(td){
12123         this.td = td;
12124         Roo.Toolbar.Button.superclass.render.call(this, td);
12125     },
12126     /**
12127      * Removes and destroys this item.
12128      */
12129     destroy : function(){
12130         this.td.parentNode.removeChild(this.td);
12131     },
12132     
12133     /**
12134      * Shows this item.
12135      */
12136     show: function(){
12137         this.hidden = false;
12138         this.td.style.display = "";
12139     },
12140     
12141     /**
12142      * Hides this item.
12143      */
12144     hide: function(){
12145         this.hidden = true;
12146         this.td.style.display = "none";
12147     },
12148     
12149     /**
12150      * Convenience function for boolean show/hide.
12151      * @param {Boolean} visible true to show/false to hide
12152      */
12153     setVisible: function(visible){
12154         if(visible) {
12155             this.show();
12156         }else{
12157             this.hide();
12158         }
12159     },
12160     
12161     /**
12162      * Try to focus this item.
12163      */
12164     focus : function(){
12165         Roo.fly(this.el).focus();
12166     },
12167     
12168     /**
12169      * Disables this item.
12170      */
12171     disable : function(){
12172         Roo.fly(this.td).addClass("x-item-disabled");
12173         this.disabled = true;
12174         this.el.disabled = true;
12175     },
12176     
12177     /**
12178      * Enables this item.
12179      */
12180     enable : function(){
12181         Roo.fly(this.td).removeClass("x-item-disabled");
12182         this.disabled = false;
12183         this.el.disabled = false;
12184     }
12185 });
12186
12187
12188 /**
12189  * @class Roo.Toolbar.Separator
12190  * @extends Roo.Toolbar.Item
12191  * A simple toolbar separator class
12192  * @constructor
12193  * Creates a new Separator
12194  */
12195 Roo.Toolbar.Separator = function(){
12196     var s = document.createElement("span");
12197     s.className = "ytb-sep";
12198     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12199 };
12200 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12201     enable:Roo.emptyFn,
12202     disable:Roo.emptyFn,
12203     focus:Roo.emptyFn
12204 });
12205
12206 /**
12207  * @class Roo.Toolbar.Spacer
12208  * @extends Roo.Toolbar.Item
12209  * A simple element that adds extra horizontal space to a toolbar.
12210  * @constructor
12211  * Creates a new Spacer
12212  */
12213 Roo.Toolbar.Spacer = function(){
12214     var s = document.createElement("div");
12215     s.className = "ytb-spacer";
12216     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12217 };
12218 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12219     enable:Roo.emptyFn,
12220     disable:Roo.emptyFn,
12221     focus:Roo.emptyFn
12222 });
12223
12224 /**
12225  * @class Roo.Toolbar.Fill
12226  * @extends Roo.Toolbar.Spacer
12227  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12228  * @constructor
12229  * Creates a new Spacer
12230  */
12231 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12232     // private
12233     render : function(td){
12234         td.style.width = '100%';
12235         Roo.Toolbar.Fill.superclass.render.call(this, td);
12236     }
12237 });
12238
12239 /**
12240  * @class Roo.Toolbar.TextItem
12241  * @extends Roo.Toolbar.Item
12242  * A simple class that renders text directly into a toolbar.
12243  * @constructor
12244  * Creates a new TextItem
12245  * @param {String} text
12246  */
12247 Roo.Toolbar.TextItem = function(text){
12248     if (typeof(text) == 'object') {
12249         text = text.text;
12250     }
12251     var s = document.createElement("span");
12252     s.className = "ytb-text";
12253     s.innerHTML = text;
12254     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12255 };
12256 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12257     enable:Roo.emptyFn,
12258     disable:Roo.emptyFn,
12259     focus:Roo.emptyFn
12260 });
12261
12262 /**
12263  * @class Roo.Toolbar.Button
12264  * @extends Roo.Button
12265  * A button that renders into a toolbar.
12266  * @constructor
12267  * Creates a new Button
12268  * @param {Object} config A standard {@link Roo.Button} config object
12269  */
12270 Roo.Toolbar.Button = function(config){
12271     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12272 };
12273 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12274     render : function(td){
12275         this.td = td;
12276         Roo.Toolbar.Button.superclass.render.call(this, td);
12277     },
12278     
12279     /**
12280      * Removes and destroys this button
12281      */
12282     destroy : function(){
12283         Roo.Toolbar.Button.superclass.destroy.call(this);
12284         this.td.parentNode.removeChild(this.td);
12285     },
12286     
12287     /**
12288      * Shows this button
12289      */
12290     show: function(){
12291         this.hidden = false;
12292         this.td.style.display = "";
12293     },
12294     
12295     /**
12296      * Hides this button
12297      */
12298     hide: function(){
12299         this.hidden = true;
12300         this.td.style.display = "none";
12301     },
12302
12303     /**
12304      * Disables this item
12305      */
12306     disable : function(){
12307         Roo.fly(this.td).addClass("x-item-disabled");
12308         this.disabled = true;
12309     },
12310
12311     /**
12312      * Enables this item
12313      */
12314     enable : function(){
12315         Roo.fly(this.td).removeClass("x-item-disabled");
12316         this.disabled = false;
12317     }
12318 });
12319 // backwards compat
12320 Roo.ToolbarButton = Roo.Toolbar.Button;
12321
12322 /**
12323  * @class Roo.Toolbar.SplitButton
12324  * @extends Roo.SplitButton
12325  * A menu button that renders into a toolbar.
12326  * @constructor
12327  * Creates a new SplitButton
12328  * @param {Object} config A standard {@link Roo.SplitButton} config object
12329  */
12330 Roo.Toolbar.SplitButton = function(config){
12331     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12332 };
12333 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12334     render : function(td){
12335         this.td = td;
12336         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12337     },
12338     
12339     /**
12340      * Removes and destroys this button
12341      */
12342     destroy : function(){
12343         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12344         this.td.parentNode.removeChild(this.td);
12345     },
12346     
12347     /**
12348      * Shows this button
12349      */
12350     show: function(){
12351         this.hidden = false;
12352         this.td.style.display = "";
12353     },
12354     
12355     /**
12356      * Hides this button
12357      */
12358     hide: function(){
12359         this.hidden = true;
12360         this.td.style.display = "none";
12361     }
12362 });
12363
12364 // backwards compat
12365 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12366  * Based on:
12367  * Ext JS Library 1.1.1
12368  * Copyright(c) 2006-2007, Ext JS, LLC.
12369  *
12370  * Originally Released Under LGPL - original licence link has changed is not relivant.
12371  *
12372  * Fork - LGPL
12373  * <script type="text/javascript">
12374  */
12375  
12376 /**
12377  * @class Roo.PagingToolbar
12378  * @extends Roo.Toolbar
12379  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12380  * @constructor
12381  * Create a new PagingToolbar
12382  * @param {Object} config The config object
12383  */
12384 Roo.PagingToolbar = function(el, ds, config)
12385 {
12386     // old args format still supported... - xtype is prefered..
12387     if (typeof(el) == 'object' && el.xtype) {
12388         // created from xtype...
12389         config = el;
12390         ds = el.dataSource;
12391         el = config.container;
12392     }
12393     var items = [];
12394     if (config.items) {
12395         items = config.items;
12396         config.items = [];
12397     }
12398     
12399     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12400     this.ds = ds;
12401     this.cursor = 0;
12402     this.renderButtons(this.el);
12403     this.bind(ds);
12404     
12405     // supprot items array.
12406    
12407     Roo.each(items, function(e) {
12408         this.add(Roo.factory(e));
12409     },this);
12410     
12411 };
12412
12413 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12414     /**
12415      * @cfg {Roo.data.Store} dataSource
12416      * The underlying data store providing the paged data
12417      */
12418     /**
12419      * @cfg {String/HTMLElement/Element} container
12420      * container The id or element that will contain the toolbar
12421      */
12422     /**
12423      * @cfg {Boolean} displayInfo
12424      * True to display the displayMsg (defaults to false)
12425      */
12426     /**
12427      * @cfg {Number} pageSize
12428      * The number of records to display per page (defaults to 20)
12429      */
12430     pageSize: 20,
12431     /**
12432      * @cfg {String} displayMsg
12433      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12434      */
12435     displayMsg : 'Displaying {0} - {1} of {2}',
12436     /**
12437      * @cfg {String} emptyMsg
12438      * The message to display when no records are found (defaults to "No data to display")
12439      */
12440     emptyMsg : 'No data to display',
12441     /**
12442      * Customizable piece of the default paging text (defaults to "Page")
12443      * @type String
12444      */
12445     beforePageText : "Page",
12446     /**
12447      * Customizable piece of the default paging text (defaults to "of %0")
12448      * @type String
12449      */
12450     afterPageText : "of {0}",
12451     /**
12452      * Customizable piece of the default paging text (defaults to "First Page")
12453      * @type String
12454      */
12455     firstText : "First Page",
12456     /**
12457      * Customizable piece of the default paging text (defaults to "Previous Page")
12458      * @type String
12459      */
12460     prevText : "Previous Page",
12461     /**
12462      * Customizable piece of the default paging text (defaults to "Next Page")
12463      * @type String
12464      */
12465     nextText : "Next Page",
12466     /**
12467      * Customizable piece of the default paging text (defaults to "Last Page")
12468      * @type String
12469      */
12470     lastText : "Last Page",
12471     /**
12472      * Customizable piece of the default paging text (defaults to "Refresh")
12473      * @type String
12474      */
12475     refreshText : "Refresh",
12476
12477     // private
12478     renderButtons : function(el){
12479         Roo.PagingToolbar.superclass.render.call(this, el);
12480         this.first = this.addButton({
12481             tooltip: this.firstText,
12482             cls: "x-btn-icon x-grid-page-first",
12483             disabled: true,
12484             handler: this.onClick.createDelegate(this, ["first"])
12485         });
12486         this.prev = this.addButton({
12487             tooltip: this.prevText,
12488             cls: "x-btn-icon x-grid-page-prev",
12489             disabled: true,
12490             handler: this.onClick.createDelegate(this, ["prev"])
12491         });
12492         //this.addSeparator();
12493         this.add(this.beforePageText);
12494         this.field = Roo.get(this.addDom({
12495            tag: "input",
12496            type: "text",
12497            size: "3",
12498            value: "1",
12499            cls: "x-grid-page-number"
12500         }).el);
12501         this.field.on("keydown", this.onPagingKeydown, this);
12502         this.field.on("focus", function(){this.dom.select();});
12503         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12504         this.field.setHeight(18);
12505         //this.addSeparator();
12506         this.next = this.addButton({
12507             tooltip: this.nextText,
12508             cls: "x-btn-icon x-grid-page-next",
12509             disabled: true,
12510             handler: this.onClick.createDelegate(this, ["next"])
12511         });
12512         this.last = this.addButton({
12513             tooltip: this.lastText,
12514             cls: "x-btn-icon x-grid-page-last",
12515             disabled: true,
12516             handler: this.onClick.createDelegate(this, ["last"])
12517         });
12518         //this.addSeparator();
12519         this.loading = this.addButton({
12520             tooltip: this.refreshText,
12521             cls: "x-btn-icon x-grid-loading",
12522             handler: this.onClick.createDelegate(this, ["refresh"])
12523         });
12524
12525         if(this.displayInfo){
12526             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12527         }
12528     },
12529
12530     // private
12531     updateInfo : function(){
12532         if(this.displayEl){
12533             var count = this.ds.getCount();
12534             var msg = count == 0 ?
12535                 this.emptyMsg :
12536                 String.format(
12537                     this.displayMsg,
12538                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12539                 );
12540             this.displayEl.update(msg);
12541         }
12542     },
12543
12544     // private
12545     onLoad : function(ds, r, o){
12546        this.cursor = o.params ? o.params.start : 0;
12547        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12548
12549        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12550        this.field.dom.value = ap;
12551        this.first.setDisabled(ap == 1);
12552        this.prev.setDisabled(ap == 1);
12553        this.next.setDisabled(ap == ps);
12554        this.last.setDisabled(ap == ps);
12555        this.loading.enable();
12556        this.updateInfo();
12557     },
12558
12559     // private
12560     getPageData : function(){
12561         var total = this.ds.getTotalCount();
12562         return {
12563             total : total,
12564             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12565             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12566         };
12567     },
12568
12569     // private
12570     onLoadError : function(){
12571         this.loading.enable();
12572     },
12573
12574     // private
12575     onPagingKeydown : function(e){
12576         var k = e.getKey();
12577         var d = this.getPageData();
12578         if(k == e.RETURN){
12579             var v = this.field.dom.value, pageNum;
12580             if(!v || isNaN(pageNum = parseInt(v, 10))){
12581                 this.field.dom.value = d.activePage;
12582                 return;
12583             }
12584             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12585             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12586             e.stopEvent();
12587         }
12588         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))
12589         {
12590           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12591           this.field.dom.value = pageNum;
12592           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12593           e.stopEvent();
12594         }
12595         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12596         {
12597           var v = this.field.dom.value, pageNum; 
12598           var increment = (e.shiftKey) ? 10 : 1;
12599           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12600             increment *= -1;
12601           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12602             this.field.dom.value = d.activePage;
12603             return;
12604           }
12605           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12606           {
12607             this.field.dom.value = parseInt(v, 10) + increment;
12608             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12609             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12610           }
12611           e.stopEvent();
12612         }
12613     },
12614
12615     // private
12616     beforeLoad : function(){
12617         if(this.loading){
12618             this.loading.disable();
12619         }
12620     },
12621
12622     // private
12623     onClick : function(which){
12624         var ds = this.ds;
12625         switch(which){
12626             case "first":
12627                 ds.load({params:{start: 0, limit: this.pageSize}});
12628             break;
12629             case "prev":
12630                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12631             break;
12632             case "next":
12633                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12634             break;
12635             case "last":
12636                 var total = ds.getTotalCount();
12637                 var extra = total % this.pageSize;
12638                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12639                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12640             break;
12641             case "refresh":
12642                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12643             break;
12644         }
12645     },
12646
12647     /**
12648      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12649      * @param {Roo.data.Store} store The data store to unbind
12650      */
12651     unbind : function(ds){
12652         ds.un("beforeload", this.beforeLoad, this);
12653         ds.un("load", this.onLoad, this);
12654         ds.un("loadexception", this.onLoadError, this);
12655         ds.un("remove", this.updateInfo, this);
12656         ds.un("add", this.updateInfo, this);
12657         this.ds = undefined;
12658     },
12659
12660     /**
12661      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12662      * @param {Roo.data.Store} store The data store to bind
12663      */
12664     bind : function(ds){
12665         ds.on("beforeload", this.beforeLoad, this);
12666         ds.on("load", this.onLoad, this);
12667         ds.on("loadexception", this.onLoadError, this);
12668         ds.on("remove", this.updateInfo, this);
12669         ds.on("add", this.updateInfo, this);
12670         this.ds = ds;
12671     }
12672 });/*
12673  * Based on:
12674  * Ext JS Library 1.1.1
12675  * Copyright(c) 2006-2007, Ext JS, LLC.
12676  *
12677  * Originally Released Under LGPL - original licence link has changed is not relivant.
12678  *
12679  * Fork - LGPL
12680  * <script type="text/javascript">
12681  */
12682
12683 /**
12684  * @class Roo.Resizable
12685  * @extends Roo.util.Observable
12686  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12687  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12688  * 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
12689  * the element will be wrapped for you automatically.</p>
12690  * <p>Here is the list of valid resize handles:</p>
12691  * <pre>
12692 Value   Description
12693 ------  -------------------
12694  'n'     north
12695  's'     south
12696  'e'     east
12697  'w'     west
12698  'nw'    northwest
12699  'sw'    southwest
12700  'se'    southeast
12701  'ne'    northeast
12702  'hd'    horizontal drag
12703  'all'   all
12704 </pre>
12705  * <p>Here's an example showing the creation of a typical Resizable:</p>
12706  * <pre><code>
12707 var resizer = new Roo.Resizable("element-id", {
12708     handles: 'all',
12709     minWidth: 200,
12710     minHeight: 100,
12711     maxWidth: 500,
12712     maxHeight: 400,
12713     pinned: true
12714 });
12715 resizer.on("resize", myHandler);
12716 </code></pre>
12717  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12718  * resizer.east.setDisplayed(false);</p>
12719  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12720  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12721  * resize operation's new size (defaults to [0, 0])
12722  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12723  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12724  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12725  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12726  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12727  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12728  * @cfg {Number} width The width of the element in pixels (defaults to null)
12729  * @cfg {Number} height The height of the element in pixels (defaults to null)
12730  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12731  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12732  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12733  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12734  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12735  * in favor of the handles config option (defaults to false)
12736  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12737  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12738  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12739  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12740  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12741  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12742  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12743  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12744  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12745  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12746  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12747  * @constructor
12748  * Create a new resizable component
12749  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12750  * @param {Object} config configuration options
12751   */
12752 Roo.Resizable = function(el, config)
12753 {
12754     this.el = Roo.get(el);
12755
12756     if(config && config.wrap){
12757         config.resizeChild = this.el;
12758         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12759         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12760         this.el.setStyle("overflow", "hidden");
12761         this.el.setPositioning(config.resizeChild.getPositioning());
12762         config.resizeChild.clearPositioning();
12763         if(!config.width || !config.height){
12764             var csize = config.resizeChild.getSize();
12765             this.el.setSize(csize.width, csize.height);
12766         }
12767         if(config.pinned && !config.adjustments){
12768             config.adjustments = "auto";
12769         }
12770     }
12771
12772     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12773     this.proxy.unselectable();
12774     this.proxy.enableDisplayMode('block');
12775
12776     Roo.apply(this, config);
12777
12778     if(this.pinned){
12779         this.disableTrackOver = true;
12780         this.el.addClass("x-resizable-pinned");
12781     }
12782     // if the element isn't positioned, make it relative
12783     var position = this.el.getStyle("position");
12784     if(position != "absolute" && position != "fixed"){
12785         this.el.setStyle("position", "relative");
12786     }
12787     if(!this.handles){ // no handles passed, must be legacy style
12788         this.handles = 's,e,se';
12789         if(this.multiDirectional){
12790             this.handles += ',n,w';
12791         }
12792     }
12793     if(this.handles == "all"){
12794         this.handles = "n s e w ne nw se sw";
12795     }
12796     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12797     var ps = Roo.Resizable.positions;
12798     for(var i = 0, len = hs.length; i < len; i++){
12799         if(hs[i] && ps[hs[i]]){
12800             var pos = ps[hs[i]];
12801             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12802         }
12803     }
12804     // legacy
12805     this.corner = this.southeast;
12806     
12807     // updateBox = the box can move..
12808     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12809         this.updateBox = true;
12810     }
12811
12812     this.activeHandle = null;
12813
12814     if(this.resizeChild){
12815         if(typeof this.resizeChild == "boolean"){
12816             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12817         }else{
12818             this.resizeChild = Roo.get(this.resizeChild, true);
12819         }
12820     }
12821     
12822     if(this.adjustments == "auto"){
12823         var rc = this.resizeChild;
12824         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12825         if(rc && (hw || hn)){
12826             rc.position("relative");
12827             rc.setLeft(hw ? hw.el.getWidth() : 0);
12828             rc.setTop(hn ? hn.el.getHeight() : 0);
12829         }
12830         this.adjustments = [
12831             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12832             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12833         ];
12834     }
12835
12836     if(this.draggable){
12837         this.dd = this.dynamic ?
12838             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12839         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12840     }
12841
12842     // public events
12843     this.addEvents({
12844         /**
12845          * @event beforeresize
12846          * Fired before resize is allowed. Set enabled to false to cancel resize.
12847          * @param {Roo.Resizable} this
12848          * @param {Roo.EventObject} e The mousedown event
12849          */
12850         "beforeresize" : true,
12851         /**
12852          * @event resizing
12853          * Fired a resizing.
12854          * @param {Roo.Resizable} this
12855          * @param {Number} x The new x position
12856          * @param {Number} y The new y position
12857          * @param {Number} w The new w width
12858          * @param {Number} h The new h hight
12859          * @param {Roo.EventObject} e The mouseup event
12860          */
12861         "resizing" : true,
12862         /**
12863          * @event resize
12864          * Fired after a resize.
12865          * @param {Roo.Resizable} this
12866          * @param {Number} width The new width
12867          * @param {Number} height The new height
12868          * @param {Roo.EventObject} e The mouseup event
12869          */
12870         "resize" : true
12871     });
12872
12873     if(this.width !== null && this.height !== null){
12874         this.resizeTo(this.width, this.height);
12875     }else{
12876         this.updateChildSize();
12877     }
12878     if(Roo.isIE){
12879         this.el.dom.style.zoom = 1;
12880     }
12881     Roo.Resizable.superclass.constructor.call(this);
12882 };
12883
12884 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12885         resizeChild : false,
12886         adjustments : [0, 0],
12887         minWidth : 5,
12888         minHeight : 5,
12889         maxWidth : 10000,
12890         maxHeight : 10000,
12891         enabled : true,
12892         animate : false,
12893         duration : .35,
12894         dynamic : false,
12895         handles : false,
12896         multiDirectional : false,
12897         disableTrackOver : false,
12898         easing : 'easeOutStrong',
12899         widthIncrement : 0,
12900         heightIncrement : 0,
12901         pinned : false,
12902         width : null,
12903         height : null,
12904         preserveRatio : false,
12905         transparent: false,
12906         minX: 0,
12907         minY: 0,
12908         draggable: false,
12909
12910         /**
12911          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12912          */
12913         constrainTo: undefined,
12914         /**
12915          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12916          */
12917         resizeRegion: undefined,
12918
12919
12920     /**
12921      * Perform a manual resize
12922      * @param {Number} width
12923      * @param {Number} height
12924      */
12925     resizeTo : function(width, height){
12926         this.el.setSize(width, height);
12927         this.updateChildSize();
12928         this.fireEvent("resize", this, width, height, null);
12929     },
12930
12931     // private
12932     startSizing : function(e, handle){
12933         this.fireEvent("beforeresize", this, e);
12934         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12935
12936             if(!this.overlay){
12937                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12938                 this.overlay.unselectable();
12939                 this.overlay.enableDisplayMode("block");
12940                 this.overlay.on("mousemove", this.onMouseMove, this);
12941                 this.overlay.on("mouseup", this.onMouseUp, this);
12942             }
12943             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12944
12945             this.resizing = true;
12946             this.startBox = this.el.getBox();
12947             this.startPoint = e.getXY();
12948             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12949                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12950
12951             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12952             this.overlay.show();
12953
12954             if(this.constrainTo) {
12955                 var ct = Roo.get(this.constrainTo);
12956                 this.resizeRegion = ct.getRegion().adjust(
12957                     ct.getFrameWidth('t'),
12958                     ct.getFrameWidth('l'),
12959                     -ct.getFrameWidth('b'),
12960                     -ct.getFrameWidth('r')
12961                 );
12962             }
12963
12964             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12965             this.proxy.show();
12966             this.proxy.setBox(this.startBox);
12967             if(!this.dynamic){
12968                 this.proxy.setStyle('visibility', 'visible');
12969             }
12970         }
12971     },
12972
12973     // private
12974     onMouseDown : function(handle, e){
12975         if(this.enabled){
12976             e.stopEvent();
12977             this.activeHandle = handle;
12978             this.startSizing(e, handle);
12979         }
12980     },
12981
12982     // private
12983     onMouseUp : function(e){
12984         var size = this.resizeElement();
12985         this.resizing = false;
12986         this.handleOut();
12987         this.overlay.hide();
12988         this.proxy.hide();
12989         this.fireEvent("resize", this, size.width, size.height, e);
12990     },
12991
12992     // private
12993     updateChildSize : function(){
12994         
12995         if(this.resizeChild){
12996             var el = this.el;
12997             var child = this.resizeChild;
12998             var adj = this.adjustments;
12999             if(el.dom.offsetWidth){
13000                 var b = el.getSize(true);
13001                 child.setSize(b.width+adj[0], b.height+adj[1]);
13002             }
13003             // Second call here for IE
13004             // The first call enables instant resizing and
13005             // the second call corrects scroll bars if they
13006             // exist
13007             if(Roo.isIE){
13008                 setTimeout(function(){
13009                     if(el.dom.offsetWidth){
13010                         var b = el.getSize(true);
13011                         child.setSize(b.width+adj[0], b.height+adj[1]);
13012                     }
13013                 }, 10);
13014             }
13015         }
13016     },
13017
13018     // private
13019     snap : function(value, inc, min){
13020         if(!inc || !value) return value;
13021         var newValue = value;
13022         var m = value % inc;
13023         if(m > 0){
13024             if(m > (inc/2)){
13025                 newValue = value + (inc-m);
13026             }else{
13027                 newValue = value - m;
13028             }
13029         }
13030         return Math.max(min, newValue);
13031     },
13032
13033     // private
13034     resizeElement : function(){
13035         var box = this.proxy.getBox();
13036         if(this.updateBox){
13037             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13038         }else{
13039             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13040         }
13041         this.updateChildSize();
13042         if(!this.dynamic){
13043             this.proxy.hide();
13044         }
13045         return box;
13046     },
13047
13048     // private
13049     constrain : function(v, diff, m, mx){
13050         if(v - diff < m){
13051             diff = v - m;
13052         }else if(v - diff > mx){
13053             diff = mx - v;
13054         }
13055         return diff;
13056     },
13057
13058     // private
13059     onMouseMove : function(e){
13060         
13061         if(this.enabled){
13062             try{// try catch so if something goes wrong the user doesn't get hung
13063
13064             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13065                 return;
13066             }
13067
13068             //var curXY = this.startPoint;
13069             var curSize = this.curSize || this.startBox;
13070             var x = this.startBox.x, y = this.startBox.y;
13071             var ox = x, oy = y;
13072             var w = curSize.width, h = curSize.height;
13073             var ow = w, oh = h;
13074             var mw = this.minWidth, mh = this.minHeight;
13075             var mxw = this.maxWidth, mxh = this.maxHeight;
13076             var wi = this.widthIncrement;
13077             var hi = this.heightIncrement;
13078
13079             var eventXY = e.getXY();
13080             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13081             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13082
13083             var pos = this.activeHandle.position;
13084
13085             switch(pos){
13086                 case "east":
13087                     w += diffX;
13088                     w = Math.min(Math.max(mw, w), mxw);
13089                     break;
13090              
13091                 case "south":
13092                     h += diffY;
13093                     h = Math.min(Math.max(mh, h), mxh);
13094                     break;
13095                 case "southeast":
13096                     w += diffX;
13097                     h += diffY;
13098                     w = Math.min(Math.max(mw, w), mxw);
13099                     h = Math.min(Math.max(mh, h), mxh);
13100                     break;
13101                 case "north":
13102                     diffY = this.constrain(h, diffY, mh, mxh);
13103                     y += diffY;
13104                     h -= diffY;
13105                     break;
13106                 case "hdrag":
13107                     
13108                     if (wi) {
13109                         var adiffX = Math.abs(diffX);
13110                         var sub = (adiffX % wi); // how much 
13111                         if (sub > (wi/2)) { // far enough to snap
13112                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13113                         } else {
13114                             // remove difference.. 
13115                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13116                         }
13117                     }
13118                     x += diffX;
13119                     x = Math.max(this.minX, x);
13120                     break;
13121                 case "west":
13122                     diffX = this.constrain(w, diffX, mw, mxw);
13123                     x += diffX;
13124                     w -= diffX;
13125                     break;
13126                 case "northeast":
13127                     w += diffX;
13128                     w = Math.min(Math.max(mw, w), mxw);
13129                     diffY = this.constrain(h, diffY, mh, mxh);
13130                     y += diffY;
13131                     h -= diffY;
13132                     break;
13133                 case "northwest":
13134                     diffX = this.constrain(w, diffX, mw, mxw);
13135                     diffY = this.constrain(h, diffY, mh, mxh);
13136                     y += diffY;
13137                     h -= diffY;
13138                     x += diffX;
13139                     w -= diffX;
13140                     break;
13141                case "southwest":
13142                     diffX = this.constrain(w, diffX, mw, mxw);
13143                     h += diffY;
13144                     h = Math.min(Math.max(mh, h), mxh);
13145                     x += diffX;
13146                     w -= diffX;
13147                     break;
13148             }
13149
13150             var sw = this.snap(w, wi, mw);
13151             var sh = this.snap(h, hi, mh);
13152             if(sw != w || sh != h){
13153                 switch(pos){
13154                     case "northeast":
13155                         y -= sh - h;
13156                     break;
13157                     case "north":
13158                         y -= sh - h;
13159                         break;
13160                     case "southwest":
13161                         x -= sw - w;
13162                     break;
13163                     case "west":
13164                         x -= sw - w;
13165                         break;
13166                     case "northwest":
13167                         x -= sw - w;
13168                         y -= sh - h;
13169                     break;
13170                 }
13171                 w = sw;
13172                 h = sh;
13173             }
13174
13175             if(this.preserveRatio){
13176                 switch(pos){
13177                     case "southeast":
13178                     case "east":
13179                         h = oh * (w/ow);
13180                         h = Math.min(Math.max(mh, h), mxh);
13181                         w = ow * (h/oh);
13182                        break;
13183                     case "south":
13184                         w = ow * (h/oh);
13185                         w = Math.min(Math.max(mw, w), mxw);
13186                         h = oh * (w/ow);
13187                         break;
13188                     case "northeast":
13189                         w = ow * (h/oh);
13190                         w = Math.min(Math.max(mw, w), mxw);
13191                         h = oh * (w/ow);
13192                     break;
13193                     case "north":
13194                         var tw = w;
13195                         w = ow * (h/oh);
13196                         w = Math.min(Math.max(mw, w), mxw);
13197                         h = oh * (w/ow);
13198                         x += (tw - w) / 2;
13199                         break;
13200                     case "southwest":
13201                         h = oh * (w/ow);
13202                         h = Math.min(Math.max(mh, h), mxh);
13203                         var tw = w;
13204                         w = ow * (h/oh);
13205                         x += tw - w;
13206                         break;
13207                     case "west":
13208                         var th = h;
13209                         h = oh * (w/ow);
13210                         h = Math.min(Math.max(mh, h), mxh);
13211                         y += (th - h) / 2;
13212                         var tw = w;
13213                         w = ow * (h/oh);
13214                         x += tw - w;
13215                        break;
13216                     case "northwest":
13217                         var tw = w;
13218                         var th = h;
13219                         h = oh * (w/ow);
13220                         h = Math.min(Math.max(mh, h), mxh);
13221                         w = ow * (h/oh);
13222                         y += th - h;
13223                         x += tw - w;
13224                        break;
13225
13226                 }
13227             }
13228             if (pos == 'hdrag') {
13229                 w = ow;
13230             }
13231             this.proxy.setBounds(x, y, w, h);
13232             if(this.dynamic){
13233                 this.resizeElement();
13234             }
13235             }catch(e){}
13236         }
13237         this.fireEvent("resizing", this, x, y, w, h, e);
13238     },
13239
13240     // private
13241     handleOver : function(){
13242         if(this.enabled){
13243             this.el.addClass("x-resizable-over");
13244         }
13245     },
13246
13247     // private
13248     handleOut : function(){
13249         if(!this.resizing){
13250             this.el.removeClass("x-resizable-over");
13251         }
13252     },
13253
13254     /**
13255      * Returns the element this component is bound to.
13256      * @return {Roo.Element}
13257      */
13258     getEl : function(){
13259         return this.el;
13260     },
13261
13262     /**
13263      * Returns the resizeChild element (or null).
13264      * @return {Roo.Element}
13265      */
13266     getResizeChild : function(){
13267         return this.resizeChild;
13268     },
13269     groupHandler : function()
13270     {
13271         
13272     },
13273     /**
13274      * Destroys this resizable. If the element was wrapped and
13275      * removeEl is not true then the element remains.
13276      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13277      */
13278     destroy : function(removeEl){
13279         this.proxy.remove();
13280         if(this.overlay){
13281             this.overlay.removeAllListeners();
13282             this.overlay.remove();
13283         }
13284         var ps = Roo.Resizable.positions;
13285         for(var k in ps){
13286             if(typeof ps[k] != "function" && this[ps[k]]){
13287                 var h = this[ps[k]];
13288                 h.el.removeAllListeners();
13289                 h.el.remove();
13290             }
13291         }
13292         if(removeEl){
13293             this.el.update("");
13294             this.el.remove();
13295         }
13296     }
13297 });
13298
13299 // private
13300 // hash to map config positions to true positions
13301 Roo.Resizable.positions = {
13302     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13303     hd: "hdrag"
13304 };
13305
13306 // private
13307 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13308     if(!this.tpl){
13309         // only initialize the template if resizable is used
13310         var tpl = Roo.DomHelper.createTemplate(
13311             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13312         );
13313         tpl.compile();
13314         Roo.Resizable.Handle.prototype.tpl = tpl;
13315     }
13316     this.position = pos;
13317     this.rz = rz;
13318     // show north drag fro topdra
13319     var handlepos = pos == 'hdrag' ? 'north' : pos;
13320     
13321     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13322     if (pos == 'hdrag') {
13323         this.el.setStyle('cursor', 'pointer');
13324     }
13325     this.el.unselectable();
13326     if(transparent){
13327         this.el.setOpacity(0);
13328     }
13329     this.el.on("mousedown", this.onMouseDown, this);
13330     if(!disableTrackOver){
13331         this.el.on("mouseover", this.onMouseOver, this);
13332         this.el.on("mouseout", this.onMouseOut, this);
13333     }
13334 };
13335
13336 // private
13337 Roo.Resizable.Handle.prototype = {
13338     afterResize : function(rz){
13339         Roo.log('after?');
13340         // do nothing
13341     },
13342     // private
13343     onMouseDown : function(e){
13344         this.rz.onMouseDown(this, e);
13345     },
13346     // private
13347     onMouseOver : function(e){
13348         this.rz.handleOver(this, e);
13349     },
13350     // private
13351     onMouseOut : function(e){
13352         this.rz.handleOut(this, e);
13353     }
13354 };/*
13355  * Based on:
13356  * Ext JS Library 1.1.1
13357  * Copyright(c) 2006-2007, Ext JS, LLC.
13358  *
13359  * Originally Released Under LGPL - original licence link has changed is not relivant.
13360  *
13361  * Fork - LGPL
13362  * <script type="text/javascript">
13363  */
13364
13365 /**
13366  * @class Roo.Editor
13367  * @extends Roo.Component
13368  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13369  * @constructor
13370  * Create a new Editor
13371  * @param {Roo.form.Field} field The Field object (or descendant)
13372  * @param {Object} config The config object
13373  */
13374 Roo.Editor = function(field, config){
13375     Roo.Editor.superclass.constructor.call(this, config);
13376     this.field = field;
13377     this.addEvents({
13378         /**
13379              * @event beforestartedit
13380              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13381              * false from the handler of this event.
13382              * @param {Editor} this
13383              * @param {Roo.Element} boundEl The underlying element bound to this editor
13384              * @param {Mixed} value The field value being set
13385              */
13386         "beforestartedit" : true,
13387         /**
13388              * @event startedit
13389              * Fires when this editor is displayed
13390              * @param {Roo.Element} boundEl The underlying element bound to this editor
13391              * @param {Mixed} value The starting field value
13392              */
13393         "startedit" : true,
13394         /**
13395              * @event beforecomplete
13396              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13397              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13398              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13399              * event will not fire since no edit actually occurred.
13400              * @param {Editor} this
13401              * @param {Mixed} value The current field value
13402              * @param {Mixed} startValue The original field value
13403              */
13404         "beforecomplete" : true,
13405         /**
13406              * @event complete
13407              * Fires after editing is complete and any changed value has been written to the underlying field.
13408              * @param {Editor} this
13409              * @param {Mixed} value The current field value
13410              * @param {Mixed} startValue The original field value
13411              */
13412         "complete" : true,
13413         /**
13414          * @event specialkey
13415          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13416          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13417          * @param {Roo.form.Field} this
13418          * @param {Roo.EventObject} e The event object
13419          */
13420         "specialkey" : true
13421     });
13422 };
13423
13424 Roo.extend(Roo.Editor, Roo.Component, {
13425     /**
13426      * @cfg {Boolean/String} autosize
13427      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13428      * or "height" to adopt the height only (defaults to false)
13429      */
13430     /**
13431      * @cfg {Boolean} revertInvalid
13432      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13433      * validation fails (defaults to true)
13434      */
13435     /**
13436      * @cfg {Boolean} ignoreNoChange
13437      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13438      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13439      * will never be ignored.
13440      */
13441     /**
13442      * @cfg {Boolean} hideEl
13443      * False to keep the bound element visible while the editor is displayed (defaults to true)
13444      */
13445     /**
13446      * @cfg {Mixed} value
13447      * The data value of the underlying field (defaults to "")
13448      */
13449     value : "",
13450     /**
13451      * @cfg {String} alignment
13452      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13453      */
13454     alignment: "c-c?",
13455     /**
13456      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13457      * for bottom-right shadow (defaults to "frame")
13458      */
13459     shadow : "frame",
13460     /**
13461      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13462      */
13463     constrain : false,
13464     /**
13465      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13466      */
13467     completeOnEnter : false,
13468     /**
13469      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13470      */
13471     cancelOnEsc : false,
13472     /**
13473      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13474      */
13475     updateEl : false,
13476
13477     // private
13478     onRender : function(ct, position){
13479         this.el = new Roo.Layer({
13480             shadow: this.shadow,
13481             cls: "x-editor",
13482             parentEl : ct,
13483             shim : this.shim,
13484             shadowOffset:4,
13485             id: this.id,
13486             constrain: this.constrain
13487         });
13488         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13489         if(this.field.msgTarget != 'title'){
13490             this.field.msgTarget = 'qtip';
13491         }
13492         this.field.render(this.el);
13493         if(Roo.isGecko){
13494             this.field.el.dom.setAttribute('autocomplete', 'off');
13495         }
13496         this.field.on("specialkey", this.onSpecialKey, this);
13497         if(this.swallowKeys){
13498             this.field.el.swallowEvent(['keydown','keypress']);
13499         }
13500         this.field.show();
13501         this.field.on("blur", this.onBlur, this);
13502         if(this.field.grow){
13503             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13504         }
13505     },
13506
13507     onSpecialKey : function(field, e)
13508     {
13509         //Roo.log('editor onSpecialKey');
13510         if(this.completeOnEnter && e.getKey() == e.ENTER){
13511             e.stopEvent();
13512             this.completeEdit();
13513             return;
13514         }
13515         // do not fire special key otherwise it might hide close the editor...
13516         if(e.getKey() == e.ENTER){    
13517             return;
13518         }
13519         if(this.cancelOnEsc && e.getKey() == e.ESC){
13520             this.cancelEdit();
13521             return;
13522         } 
13523         this.fireEvent('specialkey', field, e);
13524     
13525     },
13526
13527     /**
13528      * Starts the editing process and shows the editor.
13529      * @param {String/HTMLElement/Element} el The element to edit
13530      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13531       * to the innerHTML of el.
13532      */
13533     startEdit : function(el, value){
13534         if(this.editing){
13535             this.completeEdit();
13536         }
13537         this.boundEl = Roo.get(el);
13538         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13539         if(!this.rendered){
13540             this.render(this.parentEl || document.body);
13541         }
13542         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13543             return;
13544         }
13545         this.startValue = v;
13546         this.field.setValue(v);
13547         if(this.autoSize){
13548             var sz = this.boundEl.getSize();
13549             switch(this.autoSize){
13550                 case "width":
13551                 this.setSize(sz.width,  "");
13552                 break;
13553                 case "height":
13554                 this.setSize("",  sz.height);
13555                 break;
13556                 default:
13557                 this.setSize(sz.width,  sz.height);
13558             }
13559         }
13560         this.el.alignTo(this.boundEl, this.alignment);
13561         this.editing = true;
13562         if(Roo.QuickTips){
13563             Roo.QuickTips.disable();
13564         }
13565         this.show();
13566     },
13567
13568     /**
13569      * Sets the height and width of this editor.
13570      * @param {Number} width The new width
13571      * @param {Number} height The new height
13572      */
13573     setSize : function(w, h){
13574         this.field.setSize(w, h);
13575         if(this.el){
13576             this.el.sync();
13577         }
13578     },
13579
13580     /**
13581      * Realigns the editor to the bound field based on the current alignment config value.
13582      */
13583     realign : function(){
13584         this.el.alignTo(this.boundEl, this.alignment);
13585     },
13586
13587     /**
13588      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13589      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13590      */
13591     completeEdit : function(remainVisible){
13592         if(!this.editing){
13593             return;
13594         }
13595         var v = this.getValue();
13596         if(this.revertInvalid !== false && !this.field.isValid()){
13597             v = this.startValue;
13598             this.cancelEdit(true);
13599         }
13600         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13601             this.editing = false;
13602             this.hide();
13603             return;
13604         }
13605         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13606             this.editing = false;
13607             if(this.updateEl && this.boundEl){
13608                 this.boundEl.update(v);
13609             }
13610             if(remainVisible !== true){
13611                 this.hide();
13612             }
13613             this.fireEvent("complete", this, v, this.startValue);
13614         }
13615     },
13616
13617     // private
13618     onShow : function(){
13619         this.el.show();
13620         if(this.hideEl !== false){
13621             this.boundEl.hide();
13622         }
13623         this.field.show();
13624         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13625             this.fixIEFocus = true;
13626             this.deferredFocus.defer(50, this);
13627         }else{
13628             this.field.focus();
13629         }
13630         this.fireEvent("startedit", this.boundEl, this.startValue);
13631     },
13632
13633     deferredFocus : function(){
13634         if(this.editing){
13635             this.field.focus();
13636         }
13637     },
13638
13639     /**
13640      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13641      * reverted to the original starting value.
13642      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13643      * cancel (defaults to false)
13644      */
13645     cancelEdit : function(remainVisible){
13646         if(this.editing){
13647             this.setValue(this.startValue);
13648             if(remainVisible !== true){
13649                 this.hide();
13650             }
13651         }
13652     },
13653
13654     // private
13655     onBlur : function(){
13656         if(this.allowBlur !== true && this.editing){
13657             this.completeEdit();
13658         }
13659     },
13660
13661     // private
13662     onHide : function(){
13663         if(this.editing){
13664             this.completeEdit();
13665             return;
13666         }
13667         this.field.blur();
13668         if(this.field.collapse){
13669             this.field.collapse();
13670         }
13671         this.el.hide();
13672         if(this.hideEl !== false){
13673             this.boundEl.show();
13674         }
13675         if(Roo.QuickTips){
13676             Roo.QuickTips.enable();
13677         }
13678     },
13679
13680     /**
13681      * Sets the data value of the editor
13682      * @param {Mixed} value Any valid value supported by the underlying field
13683      */
13684     setValue : function(v){
13685         this.field.setValue(v);
13686     },
13687
13688     /**
13689      * Gets the data value of the editor
13690      * @return {Mixed} The data value
13691      */
13692     getValue : function(){
13693         return this.field.getValue();
13694     }
13695 });/*
13696  * Based on:
13697  * Ext JS Library 1.1.1
13698  * Copyright(c) 2006-2007, Ext JS, LLC.
13699  *
13700  * Originally Released Under LGPL - original licence link has changed is not relivant.
13701  *
13702  * Fork - LGPL
13703  * <script type="text/javascript">
13704  */
13705  
13706 /**
13707  * @class Roo.BasicDialog
13708  * @extends Roo.util.Observable
13709  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13710  * <pre><code>
13711 var dlg = new Roo.BasicDialog("my-dlg", {
13712     height: 200,
13713     width: 300,
13714     minHeight: 100,
13715     minWidth: 150,
13716     modal: true,
13717     proxyDrag: true,
13718     shadow: true
13719 });
13720 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13721 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13722 dlg.addButton('Cancel', dlg.hide, dlg);
13723 dlg.show();
13724 </code></pre>
13725   <b>A Dialog should always be a direct child of the body element.</b>
13726  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13727  * @cfg {String} title Default text to display in the title bar (defaults to null)
13728  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13729  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13730  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13731  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13732  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13733  * (defaults to null with no animation)
13734  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13735  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13736  * property for valid values (defaults to 'all')
13737  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13738  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13739  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13740  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13741  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13742  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13743  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13744  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13745  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13746  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13747  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13748  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13749  * draggable = true (defaults to false)
13750  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13751  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13752  * shadow (defaults to false)
13753  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13754  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13755  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13756  * @cfg {Array} buttons Array of buttons
13757  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13758  * @constructor
13759  * Create a new BasicDialog.
13760  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13761  * @param {Object} config Configuration options
13762  */
13763 Roo.BasicDialog = function(el, config){
13764     this.el = Roo.get(el);
13765     var dh = Roo.DomHelper;
13766     if(!this.el && config && config.autoCreate){
13767         if(typeof config.autoCreate == "object"){
13768             if(!config.autoCreate.id){
13769                 config.autoCreate.id = el;
13770             }
13771             this.el = dh.append(document.body,
13772                         config.autoCreate, true);
13773         }else{
13774             this.el = dh.append(document.body,
13775                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13776         }
13777     }
13778     el = this.el;
13779     el.setDisplayed(true);
13780     el.hide = this.hideAction;
13781     this.id = el.id;
13782     el.addClass("x-dlg");
13783
13784     Roo.apply(this, config);
13785
13786     this.proxy = el.createProxy("x-dlg-proxy");
13787     this.proxy.hide = this.hideAction;
13788     this.proxy.setOpacity(.5);
13789     this.proxy.hide();
13790
13791     if(config.width){
13792         el.setWidth(config.width);
13793     }
13794     if(config.height){
13795         el.setHeight(config.height);
13796     }
13797     this.size = el.getSize();
13798     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13799         this.xy = [config.x,config.y];
13800     }else{
13801         this.xy = el.getCenterXY(true);
13802     }
13803     /** The header element @type Roo.Element */
13804     this.header = el.child("> .x-dlg-hd");
13805     /** The body element @type Roo.Element */
13806     this.body = el.child("> .x-dlg-bd");
13807     /** The footer element @type Roo.Element */
13808     this.footer = el.child("> .x-dlg-ft");
13809
13810     if(!this.header){
13811         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13812     }
13813     if(!this.body){
13814         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13815     }
13816
13817     this.header.unselectable();
13818     if(this.title){
13819         this.header.update(this.title);
13820     }
13821     // this element allows the dialog to be focused for keyboard event
13822     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13823     this.focusEl.swallowEvent("click", true);
13824
13825     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13826
13827     // wrap the body and footer for special rendering
13828     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13829     if(this.footer){
13830         this.bwrap.dom.appendChild(this.footer.dom);
13831     }
13832
13833     this.bg = this.el.createChild({
13834         tag: "div", cls:"x-dlg-bg",
13835         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13836     });
13837     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13838
13839
13840     if(this.autoScroll !== false && !this.autoTabs){
13841         this.body.setStyle("overflow", "auto");
13842     }
13843
13844     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13845
13846     if(this.closable !== false){
13847         this.el.addClass("x-dlg-closable");
13848         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13849         this.close.on("click", this.closeClick, this);
13850         this.close.addClassOnOver("x-dlg-close-over");
13851     }
13852     if(this.collapsible !== false){
13853         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13854         this.collapseBtn.on("click", this.collapseClick, this);
13855         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13856         this.header.on("dblclick", this.collapseClick, this);
13857     }
13858     if(this.resizable !== false){
13859         this.el.addClass("x-dlg-resizable");
13860         this.resizer = new Roo.Resizable(el, {
13861             minWidth: this.minWidth || 80,
13862             minHeight:this.minHeight || 80,
13863             handles: this.resizeHandles || "all",
13864             pinned: true
13865         });
13866         this.resizer.on("beforeresize", this.beforeResize, this);
13867         this.resizer.on("resize", this.onResize, this);
13868     }
13869     if(this.draggable !== false){
13870         el.addClass("x-dlg-draggable");
13871         if (!this.proxyDrag) {
13872             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13873         }
13874         else {
13875             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13876         }
13877         dd.setHandleElId(this.header.id);
13878         dd.endDrag = this.endMove.createDelegate(this);
13879         dd.startDrag = this.startMove.createDelegate(this);
13880         dd.onDrag = this.onDrag.createDelegate(this);
13881         dd.scroll = false;
13882         this.dd = dd;
13883     }
13884     if(this.modal){
13885         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13886         this.mask.enableDisplayMode("block");
13887         this.mask.hide();
13888         this.el.addClass("x-dlg-modal");
13889     }
13890     if(this.shadow){
13891         this.shadow = new Roo.Shadow({
13892             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13893             offset : this.shadowOffset
13894         });
13895     }else{
13896         this.shadowOffset = 0;
13897     }
13898     if(Roo.useShims && this.shim !== false){
13899         this.shim = this.el.createShim();
13900         this.shim.hide = this.hideAction;
13901         this.shim.hide();
13902     }else{
13903         this.shim = false;
13904     }
13905     if(this.autoTabs){
13906         this.initTabs();
13907     }
13908     if (this.buttons) { 
13909         var bts= this.buttons;
13910         this.buttons = [];
13911         Roo.each(bts, function(b) {
13912             this.addButton(b);
13913         }, this);
13914     }
13915     
13916     
13917     this.addEvents({
13918         /**
13919          * @event keydown
13920          * Fires when a key is pressed
13921          * @param {Roo.BasicDialog} this
13922          * @param {Roo.EventObject} e
13923          */
13924         "keydown" : true,
13925         /**
13926          * @event move
13927          * Fires when this dialog is moved by the user.
13928          * @param {Roo.BasicDialog} this
13929          * @param {Number} x The new page X
13930          * @param {Number} y The new page Y
13931          */
13932         "move" : true,
13933         /**
13934          * @event resize
13935          * Fires when this dialog is resized by the user.
13936          * @param {Roo.BasicDialog} this
13937          * @param {Number} width The new width
13938          * @param {Number} height The new height
13939          */
13940         "resize" : true,
13941         /**
13942          * @event beforehide
13943          * Fires before this dialog is hidden.
13944          * @param {Roo.BasicDialog} this
13945          */
13946         "beforehide" : true,
13947         /**
13948          * @event hide
13949          * Fires when this dialog is hidden.
13950          * @param {Roo.BasicDialog} this
13951          */
13952         "hide" : true,
13953         /**
13954          * @event beforeshow
13955          * Fires before this dialog is shown.
13956          * @param {Roo.BasicDialog} this
13957          */
13958         "beforeshow" : true,
13959         /**
13960          * @event show
13961          * Fires when this dialog is shown.
13962          * @param {Roo.BasicDialog} this
13963          */
13964         "show" : true
13965     });
13966     el.on("keydown", this.onKeyDown, this);
13967     el.on("mousedown", this.toFront, this);
13968     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13969     this.el.hide();
13970     Roo.DialogManager.register(this);
13971     Roo.BasicDialog.superclass.constructor.call(this);
13972 };
13973
13974 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13975     shadowOffset: Roo.isIE ? 6 : 5,
13976     minHeight: 80,
13977     minWidth: 200,
13978     minButtonWidth: 75,
13979     defaultButton: null,
13980     buttonAlign: "right",
13981     tabTag: 'div',
13982     firstShow: true,
13983
13984     /**
13985      * Sets the dialog title text
13986      * @param {String} text The title text to display
13987      * @return {Roo.BasicDialog} this
13988      */
13989     setTitle : function(text){
13990         this.header.update(text);
13991         return this;
13992     },
13993
13994     // private
13995     closeClick : function(){
13996         this.hide();
13997     },
13998
13999     // private
14000     collapseClick : function(){
14001         this[this.collapsed ? "expand" : "collapse"]();
14002     },
14003
14004     /**
14005      * Collapses the dialog to its minimized state (only the title bar is visible).
14006      * Equivalent to the user clicking the collapse dialog button.
14007      */
14008     collapse : function(){
14009         if(!this.collapsed){
14010             this.collapsed = true;
14011             this.el.addClass("x-dlg-collapsed");
14012             this.restoreHeight = this.el.getHeight();
14013             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14014         }
14015     },
14016
14017     /**
14018      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14019      * clicking the expand dialog button.
14020      */
14021     expand : function(){
14022         if(this.collapsed){
14023             this.collapsed = false;
14024             this.el.removeClass("x-dlg-collapsed");
14025             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14026         }
14027     },
14028
14029     /**
14030      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14031      * @return {Roo.TabPanel} The tabs component
14032      */
14033     initTabs : function(){
14034         var tabs = this.getTabs();
14035         while(tabs.getTab(0)){
14036             tabs.removeTab(0);
14037         }
14038         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14039             var dom = el.dom;
14040             tabs.addTab(Roo.id(dom), dom.title);
14041             dom.title = "";
14042         });
14043         tabs.activate(0);
14044         return tabs;
14045     },
14046
14047     // private
14048     beforeResize : function(){
14049         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14050     },
14051
14052     // private
14053     onResize : function(){
14054         this.refreshSize();
14055         this.syncBodyHeight();
14056         this.adjustAssets();
14057         this.focus();
14058         this.fireEvent("resize", this, this.size.width, this.size.height);
14059     },
14060
14061     // private
14062     onKeyDown : function(e){
14063         if(this.isVisible()){
14064             this.fireEvent("keydown", this, e);
14065         }
14066     },
14067
14068     /**
14069      * Resizes the dialog.
14070      * @param {Number} width
14071      * @param {Number} height
14072      * @return {Roo.BasicDialog} this
14073      */
14074     resizeTo : function(width, height){
14075         this.el.setSize(width, height);
14076         this.size = {width: width, height: height};
14077         this.syncBodyHeight();
14078         if(this.fixedcenter){
14079             this.center();
14080         }
14081         if(this.isVisible()){
14082             this.constrainXY();
14083             this.adjustAssets();
14084         }
14085         this.fireEvent("resize", this, width, height);
14086         return this;
14087     },
14088
14089
14090     /**
14091      * Resizes the dialog to fit the specified content size.
14092      * @param {Number} width
14093      * @param {Number} height
14094      * @return {Roo.BasicDialog} this
14095      */
14096     setContentSize : function(w, h){
14097         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14098         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14099         //if(!this.el.isBorderBox()){
14100             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14101             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14102         //}
14103         if(this.tabs){
14104             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14105             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14106         }
14107         this.resizeTo(w, h);
14108         return this;
14109     },
14110
14111     /**
14112      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14113      * executed in response to a particular key being pressed while the dialog is active.
14114      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14115      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14116      * @param {Function} fn The function to call
14117      * @param {Object} scope (optional) The scope of the function
14118      * @return {Roo.BasicDialog} this
14119      */
14120     addKeyListener : function(key, fn, scope){
14121         var keyCode, shift, ctrl, alt;
14122         if(typeof key == "object" && !(key instanceof Array)){
14123             keyCode = key["key"];
14124             shift = key["shift"];
14125             ctrl = key["ctrl"];
14126             alt = key["alt"];
14127         }else{
14128             keyCode = key;
14129         }
14130         var handler = function(dlg, e){
14131             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14132                 var k = e.getKey();
14133                 if(keyCode instanceof Array){
14134                     for(var i = 0, len = keyCode.length; i < len; i++){
14135                         if(keyCode[i] == k){
14136                           fn.call(scope || window, dlg, k, e);
14137                           return;
14138                         }
14139                     }
14140                 }else{
14141                     if(k == keyCode){
14142                         fn.call(scope || window, dlg, k, e);
14143                     }
14144                 }
14145             }
14146         };
14147         this.on("keydown", handler);
14148         return this;
14149     },
14150
14151     /**
14152      * Returns the TabPanel component (creates it if it doesn't exist).
14153      * Note: If you wish to simply check for the existence of tabs without creating them,
14154      * check for a null 'tabs' property.
14155      * @return {Roo.TabPanel} The tabs component
14156      */
14157     getTabs : function(){
14158         if(!this.tabs){
14159             this.el.addClass("x-dlg-auto-tabs");
14160             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14161             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14162         }
14163         return this.tabs;
14164     },
14165
14166     /**
14167      * Adds a button to the footer section of the dialog.
14168      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14169      * object or a valid Roo.DomHelper element config
14170      * @param {Function} handler The function called when the button is clicked
14171      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14172      * @return {Roo.Button} The new button
14173      */
14174     addButton : function(config, handler, scope){
14175         var dh = Roo.DomHelper;
14176         if(!this.footer){
14177             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14178         }
14179         if(!this.btnContainer){
14180             var tb = this.footer.createChild({
14181
14182                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14183                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14184             }, null, true);
14185             this.btnContainer = tb.firstChild.firstChild.firstChild;
14186         }
14187         var bconfig = {
14188             handler: handler,
14189             scope: scope,
14190             minWidth: this.minButtonWidth,
14191             hideParent:true
14192         };
14193         if(typeof config == "string"){
14194             bconfig.text = config;
14195         }else{
14196             if(config.tag){
14197                 bconfig.dhconfig = config;
14198             }else{
14199                 Roo.apply(bconfig, config);
14200             }
14201         }
14202         var fc = false;
14203         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14204             bconfig.position = Math.max(0, bconfig.position);
14205             fc = this.btnContainer.childNodes[bconfig.position];
14206         }
14207          
14208         var btn = new Roo.Button(
14209             fc ? 
14210                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14211                 : this.btnContainer.appendChild(document.createElement("td")),
14212             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14213             bconfig
14214         );
14215         this.syncBodyHeight();
14216         if(!this.buttons){
14217             /**
14218              * Array of all the buttons that have been added to this dialog via addButton
14219              * @type Array
14220              */
14221             this.buttons = [];
14222         }
14223         this.buttons.push(btn);
14224         return btn;
14225     },
14226
14227     /**
14228      * Sets the default button to be focused when the dialog is displayed.
14229      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14230      * @return {Roo.BasicDialog} this
14231      */
14232     setDefaultButton : function(btn){
14233         this.defaultButton = btn;
14234         return this;
14235     },
14236
14237     // private
14238     getHeaderFooterHeight : function(safe){
14239         var height = 0;
14240         if(this.header){
14241            height += this.header.getHeight();
14242         }
14243         if(this.footer){
14244            var fm = this.footer.getMargins();
14245             height += (this.footer.getHeight()+fm.top+fm.bottom);
14246         }
14247         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14248         height += this.centerBg.getPadding("tb");
14249         return height;
14250     },
14251
14252     // private
14253     syncBodyHeight : function()
14254     {
14255         var bd = this.body, // the text
14256             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14257             bw = this.bwrap;
14258         var height = this.size.height - this.getHeaderFooterHeight(false);
14259         bd.setHeight(height-bd.getMargins("tb"));
14260         var hh = this.header.getHeight();
14261         var h = this.size.height-hh;
14262         cb.setHeight(h);
14263         
14264         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14265         bw.setHeight(h-cb.getPadding("tb"));
14266         
14267         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14268         bd.setWidth(bw.getWidth(true));
14269         if(this.tabs){
14270             this.tabs.syncHeight();
14271             if(Roo.isIE){
14272                 this.tabs.el.repaint();
14273             }
14274         }
14275     },
14276
14277     /**
14278      * Restores the previous state of the dialog if Roo.state is configured.
14279      * @return {Roo.BasicDialog} this
14280      */
14281     restoreState : function(){
14282         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14283         if(box && box.width){
14284             this.xy = [box.x, box.y];
14285             this.resizeTo(box.width, box.height);
14286         }
14287         return this;
14288     },
14289
14290     // private
14291     beforeShow : function(){
14292         this.expand();
14293         if(this.fixedcenter){
14294             this.xy = this.el.getCenterXY(true);
14295         }
14296         if(this.modal){
14297             Roo.get(document.body).addClass("x-body-masked");
14298             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14299             this.mask.show();
14300         }
14301         this.constrainXY();
14302     },
14303
14304     // private
14305     animShow : function(){
14306         var b = Roo.get(this.animateTarget).getBox();
14307         this.proxy.setSize(b.width, b.height);
14308         this.proxy.setLocation(b.x, b.y);
14309         this.proxy.show();
14310         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14311                     true, .35, this.showEl.createDelegate(this));
14312     },
14313
14314     /**
14315      * Shows the dialog.
14316      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14317      * @return {Roo.BasicDialog} this
14318      */
14319     show : function(animateTarget){
14320         if (this.fireEvent("beforeshow", this) === false){
14321             return;
14322         }
14323         if(this.syncHeightBeforeShow){
14324             this.syncBodyHeight();
14325         }else if(this.firstShow){
14326             this.firstShow = false;
14327             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14328         }
14329         this.animateTarget = animateTarget || this.animateTarget;
14330         if(!this.el.isVisible()){
14331             this.beforeShow();
14332             if(this.animateTarget && Roo.get(this.animateTarget)){
14333                 this.animShow();
14334             }else{
14335                 this.showEl();
14336             }
14337         }
14338         return this;
14339     },
14340
14341     // private
14342     showEl : function(){
14343         this.proxy.hide();
14344         this.el.setXY(this.xy);
14345         this.el.show();
14346         this.adjustAssets(true);
14347         this.toFront();
14348         this.focus();
14349         // IE peekaboo bug - fix found by Dave Fenwick
14350         if(Roo.isIE){
14351             this.el.repaint();
14352         }
14353         this.fireEvent("show", this);
14354     },
14355
14356     /**
14357      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14358      * dialog itself will receive focus.
14359      */
14360     focus : function(){
14361         if(this.defaultButton){
14362             this.defaultButton.focus();
14363         }else{
14364             this.focusEl.focus();
14365         }
14366     },
14367
14368     // private
14369     constrainXY : function(){
14370         if(this.constraintoviewport !== false){
14371             if(!this.viewSize){
14372                 if(this.container){
14373                     var s = this.container.getSize();
14374                     this.viewSize = [s.width, s.height];
14375                 }else{
14376                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14377                 }
14378             }
14379             var s = Roo.get(this.container||document).getScroll();
14380
14381             var x = this.xy[0], y = this.xy[1];
14382             var w = this.size.width, h = this.size.height;
14383             var vw = this.viewSize[0], vh = this.viewSize[1];
14384             // only move it if it needs it
14385             var moved = false;
14386             // first validate right/bottom
14387             if(x + w > vw+s.left){
14388                 x = vw - w;
14389                 moved = true;
14390             }
14391             if(y + h > vh+s.top){
14392                 y = vh - h;
14393                 moved = true;
14394             }
14395             // then make sure top/left isn't negative
14396             if(x < s.left){
14397                 x = s.left;
14398                 moved = true;
14399             }
14400             if(y < s.top){
14401                 y = s.top;
14402                 moved = true;
14403             }
14404             if(moved){
14405                 // cache xy
14406                 this.xy = [x, y];
14407                 if(this.isVisible()){
14408                     this.el.setLocation(x, y);
14409                     this.adjustAssets();
14410                 }
14411             }
14412         }
14413     },
14414
14415     // private
14416     onDrag : function(){
14417         if(!this.proxyDrag){
14418             this.xy = this.el.getXY();
14419             this.adjustAssets();
14420         }
14421     },
14422
14423     // private
14424     adjustAssets : function(doShow){
14425         var x = this.xy[0], y = this.xy[1];
14426         var w = this.size.width, h = this.size.height;
14427         if(doShow === true){
14428             if(this.shadow){
14429                 this.shadow.show(this.el);
14430             }
14431             if(this.shim){
14432                 this.shim.show();
14433             }
14434         }
14435         if(this.shadow && this.shadow.isVisible()){
14436             this.shadow.show(this.el);
14437         }
14438         if(this.shim && this.shim.isVisible()){
14439             this.shim.setBounds(x, y, w, h);
14440         }
14441     },
14442
14443     // private
14444     adjustViewport : function(w, h){
14445         if(!w || !h){
14446             w = Roo.lib.Dom.getViewWidth();
14447             h = Roo.lib.Dom.getViewHeight();
14448         }
14449         // cache the size
14450         this.viewSize = [w, h];
14451         if(this.modal && this.mask.isVisible()){
14452             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14453             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14454         }
14455         if(this.isVisible()){
14456             this.constrainXY();
14457         }
14458     },
14459
14460     /**
14461      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14462      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14463      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14464      */
14465     destroy : function(removeEl){
14466         if(this.isVisible()){
14467             this.animateTarget = null;
14468             this.hide();
14469         }
14470         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14471         if(this.tabs){
14472             this.tabs.destroy(removeEl);
14473         }
14474         Roo.destroy(
14475              this.shim,
14476              this.proxy,
14477              this.resizer,
14478              this.close,
14479              this.mask
14480         );
14481         if(this.dd){
14482             this.dd.unreg();
14483         }
14484         if(this.buttons){
14485            for(var i = 0, len = this.buttons.length; i < len; i++){
14486                this.buttons[i].destroy();
14487            }
14488         }
14489         this.el.removeAllListeners();
14490         if(removeEl === true){
14491             this.el.update("");
14492             this.el.remove();
14493         }
14494         Roo.DialogManager.unregister(this);
14495     },
14496
14497     // private
14498     startMove : function(){
14499         if(this.proxyDrag){
14500             this.proxy.show();
14501         }
14502         if(this.constraintoviewport !== false){
14503             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14504         }
14505     },
14506
14507     // private
14508     endMove : function(){
14509         if(!this.proxyDrag){
14510             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14511         }else{
14512             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14513             this.proxy.hide();
14514         }
14515         this.refreshSize();
14516         this.adjustAssets();
14517         this.focus();
14518         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14519     },
14520
14521     /**
14522      * Brings this dialog to the front of any other visible dialogs
14523      * @return {Roo.BasicDialog} this
14524      */
14525     toFront : function(){
14526         Roo.DialogManager.bringToFront(this);
14527         return this;
14528     },
14529
14530     /**
14531      * Sends this dialog to the back (under) of any other visible dialogs
14532      * @return {Roo.BasicDialog} this
14533      */
14534     toBack : function(){
14535         Roo.DialogManager.sendToBack(this);
14536         return this;
14537     },
14538
14539     /**
14540      * Centers this dialog in the viewport
14541      * @return {Roo.BasicDialog} this
14542      */
14543     center : function(){
14544         var xy = this.el.getCenterXY(true);
14545         this.moveTo(xy[0], xy[1]);
14546         return this;
14547     },
14548
14549     /**
14550      * Moves the dialog's top-left corner to the specified point
14551      * @param {Number} x
14552      * @param {Number} y
14553      * @return {Roo.BasicDialog} this
14554      */
14555     moveTo : function(x, y){
14556         this.xy = [x,y];
14557         if(this.isVisible()){
14558             this.el.setXY(this.xy);
14559             this.adjustAssets();
14560         }
14561         return this;
14562     },
14563
14564     /**
14565      * Aligns the dialog to the specified element
14566      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14567      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14568      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14569      * @return {Roo.BasicDialog} this
14570      */
14571     alignTo : function(element, position, offsets){
14572         this.xy = this.el.getAlignToXY(element, position, offsets);
14573         if(this.isVisible()){
14574             this.el.setXY(this.xy);
14575             this.adjustAssets();
14576         }
14577         return this;
14578     },
14579
14580     /**
14581      * Anchors an element to another element and realigns it when the window is resized.
14582      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14583      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14584      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14585      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14586      * is a number, it is used as the buffer delay (defaults to 50ms).
14587      * @return {Roo.BasicDialog} this
14588      */
14589     anchorTo : function(el, alignment, offsets, monitorScroll){
14590         var action = function(){
14591             this.alignTo(el, alignment, offsets);
14592         };
14593         Roo.EventManager.onWindowResize(action, this);
14594         var tm = typeof monitorScroll;
14595         if(tm != 'undefined'){
14596             Roo.EventManager.on(window, 'scroll', action, this,
14597                 {buffer: tm == 'number' ? monitorScroll : 50});
14598         }
14599         action.call(this);
14600         return this;
14601     },
14602
14603     /**
14604      * Returns true if the dialog is visible
14605      * @return {Boolean}
14606      */
14607     isVisible : function(){
14608         return this.el.isVisible();
14609     },
14610
14611     // private
14612     animHide : function(callback){
14613         var b = Roo.get(this.animateTarget).getBox();
14614         this.proxy.show();
14615         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14616         this.el.hide();
14617         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14618                     this.hideEl.createDelegate(this, [callback]));
14619     },
14620
14621     /**
14622      * Hides the dialog.
14623      * @param {Function} callback (optional) Function to call when the dialog is hidden
14624      * @return {Roo.BasicDialog} this
14625      */
14626     hide : function(callback){
14627         if (this.fireEvent("beforehide", this) === false){
14628             return;
14629         }
14630         if(this.shadow){
14631             this.shadow.hide();
14632         }
14633         if(this.shim) {
14634           this.shim.hide();
14635         }
14636         // sometimes animateTarget seems to get set.. causing problems...
14637         // this just double checks..
14638         if(this.animateTarget && Roo.get(this.animateTarget)) {
14639            this.animHide(callback);
14640         }else{
14641             this.el.hide();
14642             this.hideEl(callback);
14643         }
14644         return this;
14645     },
14646
14647     // private
14648     hideEl : function(callback){
14649         this.proxy.hide();
14650         if(this.modal){
14651             this.mask.hide();
14652             Roo.get(document.body).removeClass("x-body-masked");
14653         }
14654         this.fireEvent("hide", this);
14655         if(typeof callback == "function"){
14656             callback();
14657         }
14658     },
14659
14660     // private
14661     hideAction : function(){
14662         this.setLeft("-10000px");
14663         this.setTop("-10000px");
14664         this.setStyle("visibility", "hidden");
14665     },
14666
14667     // private
14668     refreshSize : function(){
14669         this.size = this.el.getSize();
14670         this.xy = this.el.getXY();
14671         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14672     },
14673
14674     // private
14675     // z-index is managed by the DialogManager and may be overwritten at any time
14676     setZIndex : function(index){
14677         if(this.modal){
14678             this.mask.setStyle("z-index", index);
14679         }
14680         if(this.shim){
14681             this.shim.setStyle("z-index", ++index);
14682         }
14683         if(this.shadow){
14684             this.shadow.setZIndex(++index);
14685         }
14686         this.el.setStyle("z-index", ++index);
14687         if(this.proxy){
14688             this.proxy.setStyle("z-index", ++index);
14689         }
14690         if(this.resizer){
14691             this.resizer.proxy.setStyle("z-index", ++index);
14692         }
14693
14694         this.lastZIndex = index;
14695     },
14696
14697     /**
14698      * Returns the element for this dialog
14699      * @return {Roo.Element} The underlying dialog Element
14700      */
14701     getEl : function(){
14702         return this.el;
14703     }
14704 });
14705
14706 /**
14707  * @class Roo.DialogManager
14708  * Provides global access to BasicDialogs that have been created and
14709  * support for z-indexing (layering) multiple open dialogs.
14710  */
14711 Roo.DialogManager = function(){
14712     var list = {};
14713     var accessList = [];
14714     var front = null;
14715
14716     // private
14717     var sortDialogs = function(d1, d2){
14718         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14719     };
14720
14721     // private
14722     var orderDialogs = function(){
14723         accessList.sort(sortDialogs);
14724         var seed = Roo.DialogManager.zseed;
14725         for(var i = 0, len = accessList.length; i < len; i++){
14726             var dlg = accessList[i];
14727             if(dlg){
14728                 dlg.setZIndex(seed + (i*10));
14729             }
14730         }
14731     };
14732
14733     return {
14734         /**
14735          * The starting z-index for BasicDialogs (defaults to 9000)
14736          * @type Number The z-index value
14737          */
14738         zseed : 9000,
14739
14740         // private
14741         register : function(dlg){
14742             list[dlg.id] = dlg;
14743             accessList.push(dlg);
14744         },
14745
14746         // private
14747         unregister : function(dlg){
14748             delete list[dlg.id];
14749             var i=0;
14750             var len=0;
14751             if(!accessList.indexOf){
14752                 for(  i = 0, len = accessList.length; i < len; i++){
14753                     if(accessList[i] == dlg){
14754                         accessList.splice(i, 1);
14755                         return;
14756                     }
14757                 }
14758             }else{
14759                  i = accessList.indexOf(dlg);
14760                 if(i != -1){
14761                     accessList.splice(i, 1);
14762                 }
14763             }
14764         },
14765
14766         /**
14767          * Gets a registered dialog by id
14768          * @param {String/Object} id The id of the dialog or a dialog
14769          * @return {Roo.BasicDialog} this
14770          */
14771         get : function(id){
14772             return typeof id == "object" ? id : list[id];
14773         },
14774
14775         /**
14776          * Brings the specified dialog to the front
14777          * @param {String/Object} dlg The id of the dialog or a dialog
14778          * @return {Roo.BasicDialog} this
14779          */
14780         bringToFront : function(dlg){
14781             dlg = this.get(dlg);
14782             if(dlg != front){
14783                 front = dlg;
14784                 dlg._lastAccess = new Date().getTime();
14785                 orderDialogs();
14786             }
14787             return dlg;
14788         },
14789
14790         /**
14791          * Sends the specified dialog to the back
14792          * @param {String/Object} dlg The id of the dialog or a dialog
14793          * @return {Roo.BasicDialog} this
14794          */
14795         sendToBack : function(dlg){
14796             dlg = this.get(dlg);
14797             dlg._lastAccess = -(new Date().getTime());
14798             orderDialogs();
14799             return dlg;
14800         },
14801
14802         /**
14803          * Hides all dialogs
14804          */
14805         hideAll : function(){
14806             for(var id in list){
14807                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14808                     list[id].hide();
14809                 }
14810             }
14811         }
14812     };
14813 }();
14814
14815 /**
14816  * @class Roo.LayoutDialog
14817  * @extends Roo.BasicDialog
14818  * Dialog which provides adjustments for working with a layout in a Dialog.
14819  * Add your necessary layout config options to the dialog's config.<br>
14820  * Example usage (including a nested layout):
14821  * <pre><code>
14822 if(!dialog){
14823     dialog = new Roo.LayoutDialog("download-dlg", {
14824         modal: true,
14825         width:600,
14826         height:450,
14827         shadow:true,
14828         minWidth:500,
14829         minHeight:350,
14830         autoTabs:true,
14831         proxyDrag:true,
14832         // layout config merges with the dialog config
14833         center:{
14834             tabPosition: "top",
14835             alwaysShowTabs: true
14836         }
14837     });
14838     dialog.addKeyListener(27, dialog.hide, dialog);
14839     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14840     dialog.addButton("Build It!", this.getDownload, this);
14841
14842     // we can even add nested layouts
14843     var innerLayout = new Roo.BorderLayout("dl-inner", {
14844         east: {
14845             initialSize: 200,
14846             autoScroll:true,
14847             split:true
14848         },
14849         center: {
14850             autoScroll:true
14851         }
14852     });
14853     innerLayout.beginUpdate();
14854     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14855     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14856     innerLayout.endUpdate(true);
14857
14858     var layout = dialog.getLayout();
14859     layout.beginUpdate();
14860     layout.add("center", new Roo.ContentPanel("standard-panel",
14861                         {title: "Download the Source", fitToFrame:true}));
14862     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14863                {title: "Build your own roo.js"}));
14864     layout.getRegion("center").showPanel(sp);
14865     layout.endUpdate();
14866 }
14867 </code></pre>
14868     * @constructor
14869     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14870     * @param {Object} config configuration options
14871   */
14872 Roo.LayoutDialog = function(el, cfg){
14873     
14874     var config=  cfg;
14875     if (typeof(cfg) == 'undefined') {
14876         config = Roo.apply({}, el);
14877         // not sure why we use documentElement here.. - it should always be body.
14878         // IE7 borks horribly if we use documentElement.
14879         // webkit also does not like documentElement - it creates a body element...
14880         el = Roo.get( document.body || document.documentElement ).createChild();
14881         //config.autoCreate = true;
14882     }
14883     
14884     
14885     config.autoTabs = false;
14886     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14887     this.body.setStyle({overflow:"hidden", position:"relative"});
14888     this.layout = new Roo.BorderLayout(this.body.dom, config);
14889     this.layout.monitorWindowResize = false;
14890     this.el.addClass("x-dlg-auto-layout");
14891     // fix case when center region overwrites center function
14892     this.center = Roo.BasicDialog.prototype.center;
14893     this.on("show", this.layout.layout, this.layout, true);
14894     if (config.items) {
14895         var xitems = config.items;
14896         delete config.items;
14897         Roo.each(xitems, this.addxtype, this);
14898     }
14899     
14900     
14901 };
14902 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14903     /**
14904      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14905      * @deprecated
14906      */
14907     endUpdate : function(){
14908         this.layout.endUpdate();
14909     },
14910
14911     /**
14912      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14913      *  @deprecated
14914      */
14915     beginUpdate : function(){
14916         this.layout.beginUpdate();
14917     },
14918
14919     /**
14920      * Get the BorderLayout for this dialog
14921      * @return {Roo.BorderLayout}
14922      */
14923     getLayout : function(){
14924         return this.layout;
14925     },
14926
14927     showEl : function(){
14928         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14929         if(Roo.isIE7){
14930             this.layout.layout();
14931         }
14932     },
14933
14934     // private
14935     // Use the syncHeightBeforeShow config option to control this automatically
14936     syncBodyHeight : function(){
14937         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14938         if(this.layout){this.layout.layout();}
14939     },
14940     
14941       /**
14942      * Add an xtype element (actually adds to the layout.)
14943      * @return {Object} xdata xtype object data.
14944      */
14945     
14946     addxtype : function(c) {
14947         return this.layout.addxtype(c);
14948     }
14949 });/*
14950  * Based on:
14951  * Ext JS Library 1.1.1
14952  * Copyright(c) 2006-2007, Ext JS, LLC.
14953  *
14954  * Originally Released Under LGPL - original licence link has changed is not relivant.
14955  *
14956  * Fork - LGPL
14957  * <script type="text/javascript">
14958  */
14959  
14960 /**
14961  * @class Roo.MessageBox
14962  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14963  * Example usage:
14964  *<pre><code>
14965 // Basic alert:
14966 Roo.Msg.alert('Status', 'Changes saved successfully.');
14967
14968 // Prompt for user data:
14969 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14970     if (btn == 'ok'){
14971         // process text value...
14972     }
14973 });
14974
14975 // Show a dialog using config options:
14976 Roo.Msg.show({
14977    title:'Save Changes?',
14978    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14979    buttons: Roo.Msg.YESNOCANCEL,
14980    fn: processResult,
14981    animEl: 'elId'
14982 });
14983 </code></pre>
14984  * @singleton
14985  */
14986 Roo.MessageBox = function(){
14987     var dlg, opt, mask, waitTimer;
14988     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14989     var buttons, activeTextEl, bwidth;
14990
14991     // private
14992     var handleButton = function(button){
14993         dlg.hide();
14994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
14995     };
14996
14997     // private
14998     var handleHide = function(){
14999         if(opt && opt.cls){
15000             dlg.el.removeClass(opt.cls);
15001         }
15002         if(waitTimer){
15003             Roo.TaskMgr.stop(waitTimer);
15004             waitTimer = null;
15005         }
15006     };
15007
15008     // private
15009     var updateButtons = function(b){
15010         var width = 0;
15011         if(!b){
15012             buttons["ok"].hide();
15013             buttons["cancel"].hide();
15014             buttons["yes"].hide();
15015             buttons["no"].hide();
15016             dlg.footer.dom.style.display = 'none';
15017             return width;
15018         }
15019         dlg.footer.dom.style.display = '';
15020         for(var k in buttons){
15021             if(typeof buttons[k] != "function"){
15022                 if(b[k]){
15023                     buttons[k].show();
15024                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15025                     width += buttons[k].el.getWidth()+15;
15026                 }else{
15027                     buttons[k].hide();
15028                 }
15029             }
15030         }
15031         return width;
15032     };
15033
15034     // private
15035     var handleEsc = function(d, k, e){
15036         if(opt && opt.closable !== false){
15037             dlg.hide();
15038         }
15039         if(e){
15040             e.stopEvent();
15041         }
15042     };
15043
15044     return {
15045         /**
15046          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15047          * @return {Roo.BasicDialog} The BasicDialog element
15048          */
15049         getDialog : function(){
15050            if(!dlg){
15051                 dlg = new Roo.BasicDialog("x-msg-box", {
15052                     autoCreate : true,
15053                     shadow: true,
15054                     draggable: true,
15055                     resizable:false,
15056                     constraintoviewport:false,
15057                     fixedcenter:true,
15058                     collapsible : false,
15059                     shim:true,
15060                     modal: true,
15061                     width:400, height:100,
15062                     buttonAlign:"center",
15063                     closeClick : function(){
15064                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15065                             handleButton("no");
15066                         }else{
15067                             handleButton("cancel");
15068                         }
15069                     }
15070                 });
15071                 dlg.on("hide", handleHide);
15072                 mask = dlg.mask;
15073                 dlg.addKeyListener(27, handleEsc);
15074                 buttons = {};
15075                 var bt = this.buttonText;
15076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15080                 bodyEl = dlg.body.createChild({
15081
15082                     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>'
15083                 });
15084                 msgEl = bodyEl.dom.firstChild;
15085                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15086                 textboxEl.enableDisplayMode();
15087                 textboxEl.addKeyListener([10,13], function(){
15088                     if(dlg.isVisible() && opt && opt.buttons){
15089                         if(opt.buttons.ok){
15090                             handleButton("ok");
15091                         }else if(opt.buttons.yes){
15092                             handleButton("yes");
15093                         }
15094                     }
15095                 });
15096                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15097                 textareaEl.enableDisplayMode();
15098                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15099                 progressEl.enableDisplayMode();
15100                 var pf = progressEl.dom.firstChild;
15101                 if (pf) {
15102                     pp = Roo.get(pf.firstChild);
15103                     pp.setHeight(pf.offsetHeight);
15104                 }
15105                 
15106             }
15107             return dlg;
15108         },
15109
15110         /**
15111          * Updates the message box body text
15112          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15113          * the XHTML-compliant non-breaking space character '&amp;#160;')
15114          * @return {Roo.MessageBox} This message box
15115          */
15116         updateText : function(text){
15117             if(!dlg.isVisible() && !opt.width){
15118                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15119             }
15120             msgEl.innerHTML = text || '&#160;';
15121       
15122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15124             var w = Math.max(
15125                     Math.min(opt.width || cw , this.maxWidth), 
15126                     Math.max(opt.minWidth || this.minWidth, bwidth)
15127             );
15128             if(opt.prompt){
15129                 activeTextEl.setWidth(w);
15130             }
15131             if(dlg.isVisible()){
15132                 dlg.fixedcenter = false;
15133             }
15134             // to big, make it scroll. = But as usual stupid IE does not support
15135             // !important..
15136             
15137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15140             } else {
15141                 bodyEl.dom.style.height = '';
15142                 bodyEl.dom.style.overflowY = '';
15143             }
15144             if (cw > w) {
15145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15146             } else {
15147                 bodyEl.dom.style.overflowX = '';
15148             }
15149             
15150             dlg.setContentSize(w, bodyEl.getHeight());
15151             if(dlg.isVisible()){
15152                 dlg.fixedcenter = true;
15153             }
15154             return this;
15155         },
15156
15157         /**
15158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15162          * @return {Roo.MessageBox} This message box
15163          */
15164         updateProgress : function(value, text){
15165             if(text){
15166                 this.updateText(text);
15167             }
15168             if (pp) { // weird bug on my firefox - for some reason this is not defined
15169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15170             }
15171             return this;
15172         },        
15173
15174         /**
15175          * Returns true if the message box is currently displayed
15176          * @return {Boolean} True if the message box is visible, else false
15177          */
15178         isVisible : function(){
15179             return dlg && dlg.isVisible();  
15180         },
15181
15182         /**
15183          * Hides the message box if it is displayed
15184          */
15185         hide : function(){
15186             if(this.isVisible()){
15187                 dlg.hide();
15188             }  
15189         },
15190
15191         /**
15192          * Displays a new message box, or reinitializes an existing message box, based on the config options
15193          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15194          * The following config object properties are supported:
15195          * <pre>
15196 Property    Type             Description
15197 ----------  ---------------  ------------------------------------------------------------------------------------
15198 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15199                                    closes (defaults to undefined)
15200 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15201                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15202 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15203                                    progress and wait dialogs will ignore this property and always hide the
15204                                    close button as they can only be closed programmatically.
15205 cls               String           A custom CSS class to apply to the message box element
15206 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15207                                    displayed (defaults to 75)
15208 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15209                                    function will be btn (the name of the button that was clicked, if applicable,
15210                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15211                                    Progress and wait dialogs will ignore this option since they do not respond to
15212                                    user actions and can only be closed programmatically, so any required function
15213                                    should be called by the same code after it closes the dialog.
15214 icon              String           A CSS class that provides a background image to be used as an icon for
15215                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15216 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15217 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15218 modal             Boolean          False to allow user interaction with the page while the message box is
15219                                    displayed (defaults to true)
15220 msg               String           A string that will replace the existing message box body text (defaults
15221                                    to the XHTML-compliant non-breaking space character '&#160;')
15222 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15223 progress          Boolean          True to display a progress bar (defaults to false)
15224 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15225 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15226 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15227 title             String           The title text
15228 value             String           The string value to set into the active textbox element if displayed
15229 wait              Boolean          True to display a progress bar (defaults to false)
15230 width             Number           The width of the dialog in pixels
15231 </pre>
15232          *
15233          * Example usage:
15234          * <pre><code>
15235 Roo.Msg.show({
15236    title: 'Address',
15237    msg: 'Please enter your address:',
15238    width: 300,
15239    buttons: Roo.MessageBox.OKCANCEL,
15240    multiline: true,
15241    fn: saveAddress,
15242    animEl: 'addAddressBtn'
15243 });
15244 </code></pre>
15245          * @param {Object} config Configuration options
15246          * @return {Roo.MessageBox} This message box
15247          */
15248         show : function(options)
15249         {
15250             
15251             // this causes nightmares if you show one dialog after another
15252             // especially on callbacks..
15253              
15254             if(this.isVisible()){
15255                 
15256                 this.hide();
15257                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15258                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15259                 Roo.log("New Dialog Message:" +  options.msg )
15260                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15261                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15262                 
15263             }
15264             var d = this.getDialog();
15265             opt = options;
15266             d.setTitle(opt.title || "&#160;");
15267             d.close.setDisplayed(opt.closable !== false);
15268             activeTextEl = textboxEl;
15269             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15270             if(opt.prompt){
15271                 if(opt.multiline){
15272                     textboxEl.hide();
15273                     textareaEl.show();
15274                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15275                         opt.multiline : this.defaultTextHeight);
15276                     activeTextEl = textareaEl;
15277                 }else{
15278                     textboxEl.show();
15279                     textareaEl.hide();
15280                 }
15281             }else{
15282                 textboxEl.hide();
15283                 textareaEl.hide();
15284             }
15285             progressEl.setDisplayed(opt.progress === true);
15286             this.updateProgress(0);
15287             activeTextEl.dom.value = opt.value || "";
15288             if(opt.prompt){
15289                 dlg.setDefaultButton(activeTextEl);
15290             }else{
15291                 var bs = opt.buttons;
15292                 var db = null;
15293                 if(bs && bs.ok){
15294                     db = buttons["ok"];
15295                 }else if(bs && bs.yes){
15296                     db = buttons["yes"];
15297                 }
15298                 dlg.setDefaultButton(db);
15299             }
15300             bwidth = updateButtons(opt.buttons);
15301             this.updateText(opt.msg);
15302             if(opt.cls){
15303                 d.el.addClass(opt.cls);
15304             }
15305             d.proxyDrag = opt.proxyDrag === true;
15306             d.modal = opt.modal !== false;
15307             d.mask = opt.modal !== false ? mask : false;
15308             if(!d.isVisible()){
15309                 // force it to the end of the z-index stack so it gets a cursor in FF
15310                 document.body.appendChild(dlg.el.dom);
15311                 d.animateTarget = null;
15312                 d.show(options.animEl);
15313             }
15314             return this;
15315         },
15316
15317         /**
15318          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15319          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15320          * and closing the message box when the process is complete.
15321          * @param {String} title The title bar text
15322          * @param {String} msg The message box body text
15323          * @return {Roo.MessageBox} This message box
15324          */
15325         progress : function(title, msg){
15326             this.show({
15327                 title : title,
15328                 msg : msg,
15329                 buttons: false,
15330                 progress:true,
15331                 closable:false,
15332                 minWidth: this.minProgressWidth,
15333                 modal : true
15334             });
15335             return this;
15336         },
15337
15338         /**
15339          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15340          * If a callback function is passed it will be called after the user clicks the button, and the
15341          * id of the button that was clicked will be passed as the only parameter to the callback
15342          * (could also be the top-right close button).
15343          * @param {String} title The title bar text
15344          * @param {String} msg The message box body text
15345          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15346          * @param {Object} scope (optional) The scope of the callback function
15347          * @return {Roo.MessageBox} This message box
15348          */
15349         alert : function(title, msg, fn, scope){
15350             this.show({
15351                 title : title,
15352                 msg : msg,
15353                 buttons: this.OK,
15354                 fn: fn,
15355                 scope : scope,
15356                 modal : true
15357             });
15358             return this;
15359         },
15360
15361         /**
15362          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15363          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15364          * You are responsible for closing the message box when the process is complete.
15365          * @param {String} msg The message box body text
15366          * @param {String} title (optional) The title bar text
15367          * @return {Roo.MessageBox} This message box
15368          */
15369         wait : function(msg, title){
15370             this.show({
15371                 title : title,
15372                 msg : msg,
15373                 buttons: false,
15374                 closable:false,
15375                 progress:true,
15376                 modal:true,
15377                 width:300,
15378                 wait:true
15379             });
15380             waitTimer = Roo.TaskMgr.start({
15381                 run: function(i){
15382                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15383                 },
15384                 interval: 1000
15385             });
15386             return this;
15387         },
15388
15389         /**
15390          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15391          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15392          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15393          * @param {String} title The title bar text
15394          * @param {String} msg The message box body text
15395          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15396          * @param {Object} scope (optional) The scope of the callback function
15397          * @return {Roo.MessageBox} This message box
15398          */
15399         confirm : function(title, msg, fn, scope){
15400             this.show({
15401                 title : title,
15402                 msg : msg,
15403                 buttons: this.YESNO,
15404                 fn: fn,
15405                 scope : scope,
15406                 modal : true
15407             });
15408             return this;
15409         },
15410
15411         /**
15412          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15413          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15414          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15415          * (could also be the top-right close button) and the text that was entered will be passed as the two
15416          * parameters to the callback.
15417          * @param {String} title The title bar text
15418          * @param {String} msg The message box body text
15419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15420          * @param {Object} scope (optional) The scope of the callback function
15421          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15422          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15423          * @return {Roo.MessageBox} This message box
15424          */
15425         prompt : function(title, msg, fn, scope, multiline){
15426             this.show({
15427                 title : title,
15428                 msg : msg,
15429                 buttons: this.OKCANCEL,
15430                 fn: fn,
15431                 minWidth:250,
15432                 scope : scope,
15433                 prompt:true,
15434                 multiline: multiline,
15435                 modal : true
15436             });
15437             return this;
15438         },
15439
15440         /**
15441          * Button config that displays a single OK button
15442          * @type Object
15443          */
15444         OK : {ok:true},
15445         /**
15446          * Button config that displays Yes and No buttons
15447          * @type Object
15448          */
15449         YESNO : {yes:true, no:true},
15450         /**
15451          * Button config that displays OK and Cancel buttons
15452          * @type Object
15453          */
15454         OKCANCEL : {ok:true, cancel:true},
15455         /**
15456          * Button config that displays Yes, No and Cancel buttons
15457          * @type Object
15458          */
15459         YESNOCANCEL : {yes:true, no:true, cancel:true},
15460
15461         /**
15462          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15463          * @type Number
15464          */
15465         defaultTextHeight : 75,
15466         /**
15467          * The maximum width in pixels of the message box (defaults to 600)
15468          * @type Number
15469          */
15470         maxWidth : 600,
15471         /**
15472          * The minimum width in pixels of the message box (defaults to 100)
15473          * @type Number
15474          */
15475         minWidth : 100,
15476         /**
15477          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15478          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15479          * @type Number
15480          */
15481         minProgressWidth : 250,
15482         /**
15483          * An object containing the default button text strings that can be overriden for localized language support.
15484          * Supported properties are: ok, cancel, yes and no.
15485          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15486          * @type Object
15487          */
15488         buttonText : {
15489             ok : "OK",
15490             cancel : "Cancel",
15491             yes : "Yes",
15492             no : "No"
15493         }
15494     };
15495 }();
15496
15497 /**
15498  * Shorthand for {@link Roo.MessageBox}
15499  */
15500 Roo.Msg = Roo.MessageBox;/*
15501  * Based on:
15502  * Ext JS Library 1.1.1
15503  * Copyright(c) 2006-2007, Ext JS, LLC.
15504  *
15505  * Originally Released Under LGPL - original licence link has changed is not relivant.
15506  *
15507  * Fork - LGPL
15508  * <script type="text/javascript">
15509  */
15510 /**
15511  * @class Roo.QuickTips
15512  * Provides attractive and customizable tooltips for any element.
15513  * @singleton
15514  */
15515 Roo.QuickTips = function(){
15516     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15517     var ce, bd, xy, dd;
15518     var visible = false, disabled = true, inited = false;
15519     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15520     
15521     var onOver = function(e){
15522         if(disabled){
15523             return;
15524         }
15525         var t = e.getTarget();
15526         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15527             return;
15528         }
15529         if(ce && t == ce.el){
15530             clearTimeout(hideProc);
15531             return;
15532         }
15533         if(t && tagEls[t.id]){
15534             tagEls[t.id].el = t;
15535             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15536             return;
15537         }
15538         var ttp, et = Roo.fly(t);
15539         var ns = cfg.namespace;
15540         if(tm.interceptTitles && t.title){
15541             ttp = t.title;
15542             t.qtip = ttp;
15543             t.removeAttribute("title");
15544             e.preventDefault();
15545         }else{
15546             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15547         }
15548         if(ttp){
15549             showProc = show.defer(tm.showDelay, tm, [{
15550                 el: t, 
15551                 text: ttp, 
15552                 width: et.getAttributeNS(ns, cfg.width),
15553                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15554                 title: et.getAttributeNS(ns, cfg.title),
15555                     cls: et.getAttributeNS(ns, cfg.cls)
15556             }]);
15557         }
15558     };
15559     
15560     var onOut = function(e){
15561         clearTimeout(showProc);
15562         var t = e.getTarget();
15563         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15564             hideProc = setTimeout(hide, tm.hideDelay);
15565         }
15566     };
15567     
15568     var onMove = function(e){
15569         if(disabled){
15570             return;
15571         }
15572         xy = e.getXY();
15573         xy[1] += 18;
15574         if(tm.trackMouse && ce){
15575             el.setXY(xy);
15576         }
15577     };
15578     
15579     var onDown = function(e){
15580         clearTimeout(showProc);
15581         clearTimeout(hideProc);
15582         if(!e.within(el)){
15583             if(tm.hideOnClick){
15584                 hide();
15585                 tm.disable();
15586                 tm.enable.defer(100, tm);
15587             }
15588         }
15589     };
15590     
15591     var getPad = function(){
15592         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15593     };
15594
15595     var show = function(o){
15596         if(disabled){
15597             return;
15598         }
15599         clearTimeout(dismissProc);
15600         ce = o;
15601         if(removeCls){ // in case manually hidden
15602             el.removeClass(removeCls);
15603             removeCls = null;
15604         }
15605         if(ce.cls){
15606             el.addClass(ce.cls);
15607             removeCls = ce.cls;
15608         }
15609         if(ce.title){
15610             tipTitle.update(ce.title);
15611             tipTitle.show();
15612         }else{
15613             tipTitle.update('');
15614             tipTitle.hide();
15615         }
15616         el.dom.style.width  = tm.maxWidth+'px';
15617         //tipBody.dom.style.width = '';
15618         tipBodyText.update(o.text);
15619         var p = getPad(), w = ce.width;
15620         if(!w){
15621             var td = tipBodyText.dom;
15622             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15623             if(aw > tm.maxWidth){
15624                 w = tm.maxWidth;
15625             }else if(aw < tm.minWidth){
15626                 w = tm.minWidth;
15627             }else{
15628                 w = aw;
15629             }
15630         }
15631         //tipBody.setWidth(w);
15632         el.setWidth(parseInt(w, 10) + p);
15633         if(ce.autoHide === false){
15634             close.setDisplayed(true);
15635             if(dd){
15636                 dd.unlock();
15637             }
15638         }else{
15639             close.setDisplayed(false);
15640             if(dd){
15641                 dd.lock();
15642             }
15643         }
15644         if(xy){
15645             el.avoidY = xy[1]-18;
15646             el.setXY(xy);
15647         }
15648         if(tm.animate){
15649             el.setOpacity(.1);
15650             el.setStyle("visibility", "visible");
15651             el.fadeIn({callback: afterShow});
15652         }else{
15653             afterShow();
15654         }
15655     };
15656     
15657     var afterShow = function(){
15658         if(ce){
15659             el.show();
15660             esc.enable();
15661             if(tm.autoDismiss && ce.autoHide !== false){
15662                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15663             }
15664         }
15665     };
15666     
15667     var hide = function(noanim){
15668         clearTimeout(dismissProc);
15669         clearTimeout(hideProc);
15670         ce = null;
15671         if(el.isVisible()){
15672             esc.disable();
15673             if(noanim !== true && tm.animate){
15674                 el.fadeOut({callback: afterHide});
15675             }else{
15676                 afterHide();
15677             } 
15678         }
15679     };
15680     
15681     var afterHide = function(){
15682         el.hide();
15683         if(removeCls){
15684             el.removeClass(removeCls);
15685             removeCls = null;
15686         }
15687     };
15688     
15689     return {
15690         /**
15691         * @cfg {Number} minWidth
15692         * The minimum width of the quick tip (defaults to 40)
15693         */
15694        minWidth : 40,
15695         /**
15696         * @cfg {Number} maxWidth
15697         * The maximum width of the quick tip (defaults to 300)
15698         */
15699        maxWidth : 300,
15700         /**
15701         * @cfg {Boolean} interceptTitles
15702         * True to automatically use the element's DOM title value if available (defaults to false)
15703         */
15704        interceptTitles : false,
15705         /**
15706         * @cfg {Boolean} trackMouse
15707         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15708         */
15709        trackMouse : false,
15710         /**
15711         * @cfg {Boolean} hideOnClick
15712         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15713         */
15714        hideOnClick : true,
15715         /**
15716         * @cfg {Number} showDelay
15717         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15718         */
15719        showDelay : 500,
15720         /**
15721         * @cfg {Number} hideDelay
15722         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15723         */
15724        hideDelay : 200,
15725         /**
15726         * @cfg {Boolean} autoHide
15727         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15728         * Used in conjunction with hideDelay.
15729         */
15730        autoHide : true,
15731         /**
15732         * @cfg {Boolean}
15733         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15734         * (defaults to true).  Used in conjunction with autoDismissDelay.
15735         */
15736        autoDismiss : true,
15737         /**
15738         * @cfg {Number}
15739         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15740         */
15741        autoDismissDelay : 5000,
15742        /**
15743         * @cfg {Boolean} animate
15744         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15745         */
15746        animate : false,
15747
15748        /**
15749         * @cfg {String} title
15750         * Title text to display (defaults to '').  This can be any valid HTML markup.
15751         */
15752         title: '',
15753        /**
15754         * @cfg {String} text
15755         * Body text to display (defaults to '').  This can be any valid HTML markup.
15756         */
15757         text : '',
15758        /**
15759         * @cfg {String} cls
15760         * A CSS class to apply to the base quick tip element (defaults to '').
15761         */
15762         cls : '',
15763        /**
15764         * @cfg {Number} width
15765         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15766         * minWidth or maxWidth.
15767         */
15768         width : null,
15769
15770     /**
15771      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15772      * or display QuickTips in a page.
15773      */
15774        init : function(){
15775           tm = Roo.QuickTips;
15776           cfg = tm.tagConfig;
15777           if(!inited){
15778               if(!Roo.isReady){ // allow calling of init() before onReady
15779                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15780                   return;
15781               }
15782               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15783               el.fxDefaults = {stopFx: true};
15784               // maximum custom styling
15785               //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>');
15786               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>');              
15787               tipTitle = el.child('h3');
15788               tipTitle.enableDisplayMode("block");
15789               tipBody = el.child('div.x-tip-bd');
15790               tipBodyText = el.child('div.x-tip-bd-inner');
15791               //bdLeft = el.child('div.x-tip-bd-left');
15792               //bdRight = el.child('div.x-tip-bd-right');
15793               close = el.child('div.x-tip-close');
15794               close.enableDisplayMode("block");
15795               close.on("click", hide);
15796               var d = Roo.get(document);
15797               d.on("mousedown", onDown);
15798               d.on("mouseover", onOver);
15799               d.on("mouseout", onOut);
15800               d.on("mousemove", onMove);
15801               esc = d.addKeyListener(27, hide);
15802               esc.disable();
15803               if(Roo.dd.DD){
15804                   dd = el.initDD("default", null, {
15805                       onDrag : function(){
15806                           el.sync();  
15807                       }
15808                   });
15809                   dd.setHandleElId(tipTitle.id);
15810                   dd.lock();
15811               }
15812               inited = true;
15813           }
15814           this.enable(); 
15815        },
15816
15817     /**
15818      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15819      * are supported:
15820      * <pre>
15821 Property    Type                   Description
15822 ----------  ---------------------  ------------------------------------------------------------------------
15823 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15824      * </ul>
15825      * @param {Object} config The config object
15826      */
15827        register : function(config){
15828            var cs = config instanceof Array ? config : arguments;
15829            for(var i = 0, len = cs.length; i < len; i++) {
15830                var c = cs[i];
15831                var target = c.target;
15832                if(target){
15833                    if(target instanceof Array){
15834                        for(var j = 0, jlen = target.length; j < jlen; j++){
15835                            tagEls[target[j]] = c;
15836                        }
15837                    }else{
15838                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15839                    }
15840                }
15841            }
15842        },
15843
15844     /**
15845      * Removes this quick tip from its element and destroys it.
15846      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15847      */
15848        unregister : function(el){
15849            delete tagEls[Roo.id(el)];
15850        },
15851
15852     /**
15853      * Enable this quick tip.
15854      */
15855        enable : function(){
15856            if(inited && disabled){
15857                locks.pop();
15858                if(locks.length < 1){
15859                    disabled = false;
15860                }
15861            }
15862        },
15863
15864     /**
15865      * Disable this quick tip.
15866      */
15867        disable : function(){
15868           disabled = true;
15869           clearTimeout(showProc);
15870           clearTimeout(hideProc);
15871           clearTimeout(dismissProc);
15872           if(ce){
15873               hide(true);
15874           }
15875           locks.push(1);
15876        },
15877
15878     /**
15879      * Returns true if the quick tip is enabled, else false.
15880      */
15881        isEnabled : function(){
15882             return !disabled;
15883        },
15884
15885         // private
15886        tagConfig : {
15887            namespace : "ext",
15888            attribute : "qtip",
15889            width : "width",
15890            target : "target",
15891            title : "qtitle",
15892            hide : "hide",
15893            cls : "qclass"
15894        }
15895    };
15896 }();
15897
15898 // backwards compat
15899 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15900  * Based on:
15901  * Ext JS Library 1.1.1
15902  * Copyright(c) 2006-2007, Ext JS, LLC.
15903  *
15904  * Originally Released Under LGPL - original licence link has changed is not relivant.
15905  *
15906  * Fork - LGPL
15907  * <script type="text/javascript">
15908  */
15909  
15910
15911 /**
15912  * @class Roo.tree.TreePanel
15913  * @extends Roo.data.Tree
15914
15915  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15916  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15917  * @cfg {Boolean} enableDD true to enable drag and drop
15918  * @cfg {Boolean} enableDrag true to enable just drag
15919  * @cfg {Boolean} enableDrop true to enable just drop
15920  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15921  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15922  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15923  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15924  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15925  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15926  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15927  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15928  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15929  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15930  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15931  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15932  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15933  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15934  * @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>
15935  * @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>
15936  * 
15937  * @constructor
15938  * @param {String/HTMLElement/Element} el The container element
15939  * @param {Object} config
15940  */
15941 Roo.tree.TreePanel = function(el, config){
15942     var root = false;
15943     var loader = false;
15944     if (config.root) {
15945         root = config.root;
15946         delete config.root;
15947     }
15948     if (config.loader) {
15949         loader = config.loader;
15950         delete config.loader;
15951     }
15952     
15953     Roo.apply(this, config);
15954     Roo.tree.TreePanel.superclass.constructor.call(this);
15955     this.el = Roo.get(el);
15956     this.el.addClass('x-tree');
15957     //console.log(root);
15958     if (root) {
15959         this.setRootNode( Roo.factory(root, Roo.tree));
15960     }
15961     if (loader) {
15962         this.loader = Roo.factory(loader, Roo.tree);
15963     }
15964    /**
15965     * Read-only. The id of the container element becomes this TreePanel's id.
15966     */
15967     this.id = this.el.id;
15968     this.addEvents({
15969         /**
15970         * @event beforeload
15971         * Fires before a node is loaded, return false to cancel
15972         * @param {Node} node The node being loaded
15973         */
15974         "beforeload" : true,
15975         /**
15976         * @event load
15977         * Fires when a node is loaded
15978         * @param {Node} node The node that was loaded
15979         */
15980         "load" : true,
15981         /**
15982         * @event textchange
15983         * Fires when the text for a node is changed
15984         * @param {Node} node The node
15985         * @param {String} text The new text
15986         * @param {String} oldText The old text
15987         */
15988         "textchange" : true,
15989         /**
15990         * @event beforeexpand
15991         * Fires before a node is expanded, return false to cancel.
15992         * @param {Node} node The node
15993         * @param {Boolean} deep
15994         * @param {Boolean} anim
15995         */
15996         "beforeexpand" : true,
15997         /**
15998         * @event beforecollapse
15999         * Fires before a node is collapsed, return false to cancel.
16000         * @param {Node} node The node
16001         * @param {Boolean} deep
16002         * @param {Boolean} anim
16003         */
16004         "beforecollapse" : true,
16005         /**
16006         * @event expand
16007         * Fires when a node is expanded
16008         * @param {Node} node The node
16009         */
16010         "expand" : true,
16011         /**
16012         * @event disabledchange
16013         * Fires when the disabled status of a node changes
16014         * @param {Node} node The node
16015         * @param {Boolean} disabled
16016         */
16017         "disabledchange" : true,
16018         /**
16019         * @event collapse
16020         * Fires when a node is collapsed
16021         * @param {Node} node The node
16022         */
16023         "collapse" : true,
16024         /**
16025         * @event beforeclick
16026         * Fires before click processing on a node. Return false to cancel the default action.
16027         * @param {Node} node The node
16028         * @param {Roo.EventObject} e The event object
16029         */
16030         "beforeclick":true,
16031         /**
16032         * @event checkchange
16033         * Fires when a node with a checkbox's checked property changes
16034         * @param {Node} this This node
16035         * @param {Boolean} checked
16036         */
16037         "checkchange":true,
16038         /**
16039         * @event click
16040         * Fires when a node is clicked
16041         * @param {Node} node The node
16042         * @param {Roo.EventObject} e The event object
16043         */
16044         "click":true,
16045         /**
16046         * @event dblclick
16047         * Fires when a node is double clicked
16048         * @param {Node} node The node
16049         * @param {Roo.EventObject} e The event object
16050         */
16051         "dblclick":true,
16052         /**
16053         * @event contextmenu
16054         * Fires when a node is right clicked
16055         * @param {Node} node The node
16056         * @param {Roo.EventObject} e The event object
16057         */
16058         "contextmenu":true,
16059         /**
16060         * @event beforechildrenrendered
16061         * Fires right before the child nodes for a node are rendered
16062         * @param {Node} node The node
16063         */
16064         "beforechildrenrendered":true,
16065         /**
16066         * @event startdrag
16067         * Fires when a node starts being dragged
16068         * @param {Roo.tree.TreePanel} this
16069         * @param {Roo.tree.TreeNode} node
16070         * @param {event} e The raw browser event
16071         */ 
16072        "startdrag" : true,
16073        /**
16074         * @event enddrag
16075         * Fires when a drag operation is complete
16076         * @param {Roo.tree.TreePanel} this
16077         * @param {Roo.tree.TreeNode} node
16078         * @param {event} e The raw browser event
16079         */
16080        "enddrag" : true,
16081        /**
16082         * @event dragdrop
16083         * Fires when a dragged node is dropped on a valid DD target
16084         * @param {Roo.tree.TreePanel} this
16085         * @param {Roo.tree.TreeNode} node
16086         * @param {DD} dd The dd it was dropped on
16087         * @param {event} e The raw browser event
16088         */
16089        "dragdrop" : true,
16090        /**
16091         * @event beforenodedrop
16092         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16093         * passed to handlers has the following properties:<br />
16094         * <ul style="padding:5px;padding-left:16px;">
16095         * <li>tree - The TreePanel</li>
16096         * <li>target - The node being targeted for the drop</li>
16097         * <li>data - The drag data from the drag source</li>
16098         * <li>point - The point of the drop - append, above or below</li>
16099         * <li>source - The drag source</li>
16100         * <li>rawEvent - Raw mouse event</li>
16101         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16102         * to be inserted by setting them on this object.</li>
16103         * <li>cancel - Set this to true to cancel the drop.</li>
16104         * </ul>
16105         * @param {Object} dropEvent
16106         */
16107        "beforenodedrop" : true,
16108        /**
16109         * @event nodedrop
16110         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16111         * passed to handlers has the following properties:<br />
16112         * <ul style="padding:5px;padding-left:16px;">
16113         * <li>tree - The TreePanel</li>
16114         * <li>target - The node being targeted for the drop</li>
16115         * <li>data - The drag data from the drag source</li>
16116         * <li>point - The point of the drop - append, above or below</li>
16117         * <li>source - The drag source</li>
16118         * <li>rawEvent - Raw mouse event</li>
16119         * <li>dropNode - Dropped node(s).</li>
16120         * </ul>
16121         * @param {Object} dropEvent
16122         */
16123        "nodedrop" : true,
16124         /**
16125         * @event nodedragover
16126         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16127         * passed to handlers has the following properties:<br />
16128         * <ul style="padding:5px;padding-left:16px;">
16129         * <li>tree - The TreePanel</li>
16130         * <li>target - The node being targeted for the drop</li>
16131         * <li>data - The drag data from the drag source</li>
16132         * <li>point - The point of the drop - append, above or below</li>
16133         * <li>source - The drag source</li>
16134         * <li>rawEvent - Raw mouse event</li>
16135         * <li>dropNode - Drop node(s) provided by the source.</li>
16136         * <li>cancel - Set this to true to signal drop not allowed.</li>
16137         * </ul>
16138         * @param {Object} dragOverEvent
16139         */
16140        "nodedragover" : true
16141         
16142     });
16143     if(this.singleExpand){
16144        this.on("beforeexpand", this.restrictExpand, this);
16145     }
16146     if (this.editor) {
16147         this.editor.tree = this;
16148         this.editor = Roo.factory(this.editor, Roo.tree);
16149     }
16150     
16151     if (this.selModel) {
16152         this.selModel = Roo.factory(this.selModel, Roo.tree);
16153     }
16154    
16155 };
16156 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16157     rootVisible : true,
16158     animate: Roo.enableFx,
16159     lines : true,
16160     enableDD : false,
16161     hlDrop : Roo.enableFx,
16162   
16163     renderer: false,
16164     
16165     rendererTip: false,
16166     // private
16167     restrictExpand : function(node){
16168         var p = node.parentNode;
16169         if(p){
16170             if(p.expandedChild && p.expandedChild.parentNode == p){
16171                 p.expandedChild.collapse();
16172             }
16173             p.expandedChild = node;
16174         }
16175     },
16176
16177     // private override
16178     setRootNode : function(node){
16179         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16180         if(!this.rootVisible){
16181             node.ui = new Roo.tree.RootTreeNodeUI(node);
16182         }
16183         return node;
16184     },
16185
16186     /**
16187      * Returns the container element for this TreePanel
16188      */
16189     getEl : function(){
16190         return this.el;
16191     },
16192
16193     /**
16194      * Returns the default TreeLoader for this TreePanel
16195      */
16196     getLoader : function(){
16197         return this.loader;
16198     },
16199
16200     /**
16201      * Expand all nodes
16202      */
16203     expandAll : function(){
16204         this.root.expand(true);
16205     },
16206
16207     /**
16208      * Collapse all nodes
16209      */
16210     collapseAll : function(){
16211         this.root.collapse(true);
16212     },
16213
16214     /**
16215      * Returns the selection model used by this TreePanel
16216      */
16217     getSelectionModel : function(){
16218         if(!this.selModel){
16219             this.selModel = new Roo.tree.DefaultSelectionModel();
16220         }
16221         return this.selModel;
16222     },
16223
16224     /**
16225      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16226      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16227      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16228      * @return {Array}
16229      */
16230     getChecked : function(a, startNode){
16231         startNode = startNode || this.root;
16232         var r = [];
16233         var f = function(){
16234             if(this.attributes.checked){
16235                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16236             }
16237         }
16238         startNode.cascade(f);
16239         return r;
16240     },
16241
16242     /**
16243      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16244      * @param {String} path
16245      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16246      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16247      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16248      */
16249     expandPath : function(path, attr, callback){
16250         attr = attr || "id";
16251         var keys = path.split(this.pathSeparator);
16252         var curNode = this.root;
16253         if(curNode.attributes[attr] != keys[1]){ // invalid root
16254             if(callback){
16255                 callback(false, null);
16256             }
16257             return;
16258         }
16259         var index = 1;
16260         var f = function(){
16261             if(++index == keys.length){
16262                 if(callback){
16263                     callback(true, curNode);
16264                 }
16265                 return;
16266             }
16267             var c = curNode.findChild(attr, keys[index]);
16268             if(!c){
16269                 if(callback){
16270                     callback(false, curNode);
16271                 }
16272                 return;
16273             }
16274             curNode = c;
16275             c.expand(false, false, f);
16276         };
16277         curNode.expand(false, false, f);
16278     },
16279
16280     /**
16281      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16282      * @param {String} path
16283      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16284      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16285      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16286      */
16287     selectPath : function(path, attr, callback){
16288         attr = attr || "id";
16289         var keys = path.split(this.pathSeparator);
16290         var v = keys.pop();
16291         if(keys.length > 0){
16292             var f = function(success, node){
16293                 if(success && node){
16294                     var n = node.findChild(attr, v);
16295                     if(n){
16296                         n.select();
16297                         if(callback){
16298                             callback(true, n);
16299                         }
16300                     }else if(callback){
16301                         callback(false, n);
16302                     }
16303                 }else{
16304                     if(callback){
16305                         callback(false, n);
16306                     }
16307                 }
16308             };
16309             this.expandPath(keys.join(this.pathSeparator), attr, f);
16310         }else{
16311             this.root.select();
16312             if(callback){
16313                 callback(true, this.root);
16314             }
16315         }
16316     },
16317
16318     getTreeEl : function(){
16319         return this.el;
16320     },
16321
16322     /**
16323      * Trigger rendering of this TreePanel
16324      */
16325     render : function(){
16326         if (this.innerCt) {
16327             return this; // stop it rendering more than once!!
16328         }
16329         
16330         this.innerCt = this.el.createChild({tag:"ul",
16331                cls:"x-tree-root-ct " +
16332                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16333
16334         if(this.containerScroll){
16335             Roo.dd.ScrollManager.register(this.el);
16336         }
16337         if((this.enableDD || this.enableDrop) && !this.dropZone){
16338            /**
16339             * The dropZone used by this tree if drop is enabled
16340             * @type Roo.tree.TreeDropZone
16341             */
16342              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16343                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16344            });
16345         }
16346         if((this.enableDD || this.enableDrag) && !this.dragZone){
16347            /**
16348             * The dragZone used by this tree if drag is enabled
16349             * @type Roo.tree.TreeDragZone
16350             */
16351             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16352                ddGroup: this.ddGroup || "TreeDD",
16353                scroll: this.ddScroll
16354            });
16355         }
16356         this.getSelectionModel().init(this);
16357         if (!this.root) {
16358             Roo.log("ROOT not set in tree");
16359             return this;
16360         }
16361         this.root.render();
16362         if(!this.rootVisible){
16363             this.root.renderChildren();
16364         }
16365         return this;
16366     }
16367 });/*
16368  * Based on:
16369  * Ext JS Library 1.1.1
16370  * Copyright(c) 2006-2007, Ext JS, LLC.
16371  *
16372  * Originally Released Under LGPL - original licence link has changed is not relivant.
16373  *
16374  * Fork - LGPL
16375  * <script type="text/javascript">
16376  */
16377  
16378
16379 /**
16380  * @class Roo.tree.DefaultSelectionModel
16381  * @extends Roo.util.Observable
16382  * The default single selection for a TreePanel.
16383  * @param {Object} cfg Configuration
16384  */
16385 Roo.tree.DefaultSelectionModel = function(cfg){
16386    this.selNode = null;
16387    
16388    
16389    
16390    this.addEvents({
16391        /**
16392         * @event selectionchange
16393         * Fires when the selected node changes
16394         * @param {DefaultSelectionModel} this
16395         * @param {TreeNode} node the new selection
16396         */
16397        "selectionchange" : true,
16398
16399        /**
16400         * @event beforeselect
16401         * Fires before the selected node changes, return false to cancel the change
16402         * @param {DefaultSelectionModel} this
16403         * @param {TreeNode} node the new selection
16404         * @param {TreeNode} node the old selection
16405         */
16406        "beforeselect" : true
16407    });
16408    
16409     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16410 };
16411
16412 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16413     init : function(tree){
16414         this.tree = tree;
16415         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16416         tree.on("click", this.onNodeClick, this);
16417     },
16418     
16419     onNodeClick : function(node, e){
16420         if (e.ctrlKey && this.selNode == node)  {
16421             this.unselect(node);
16422             return;
16423         }
16424         this.select(node);
16425     },
16426     
16427     /**
16428      * Select a node.
16429      * @param {TreeNode} node The node to select
16430      * @return {TreeNode} The selected node
16431      */
16432     select : function(node){
16433         var last = this.selNode;
16434         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16435             if(last){
16436                 last.ui.onSelectedChange(false);
16437             }
16438             this.selNode = node;
16439             node.ui.onSelectedChange(true);
16440             this.fireEvent("selectionchange", this, node, last);
16441         }
16442         return node;
16443     },
16444     
16445     /**
16446      * Deselect a node.
16447      * @param {TreeNode} node The node to unselect
16448      */
16449     unselect : function(node){
16450         if(this.selNode == node){
16451             this.clearSelections();
16452         }    
16453     },
16454     
16455     /**
16456      * Clear all selections
16457      */
16458     clearSelections : function(){
16459         var n = this.selNode;
16460         if(n){
16461             n.ui.onSelectedChange(false);
16462             this.selNode = null;
16463             this.fireEvent("selectionchange", this, null);
16464         }
16465         return n;
16466     },
16467     
16468     /**
16469      * Get the selected node
16470      * @return {TreeNode} The selected node
16471      */
16472     getSelectedNode : function(){
16473         return this.selNode;    
16474     },
16475     
16476     /**
16477      * Returns true if the node is selected
16478      * @param {TreeNode} node The node to check
16479      * @return {Boolean}
16480      */
16481     isSelected : function(node){
16482         return this.selNode == node;  
16483     },
16484
16485     /**
16486      * Selects the node above the selected node in the tree, intelligently walking the nodes
16487      * @return TreeNode The new selection
16488      */
16489     selectPrevious : function(){
16490         var s = this.selNode || this.lastSelNode;
16491         if(!s){
16492             return null;
16493         }
16494         var ps = s.previousSibling;
16495         if(ps){
16496             if(!ps.isExpanded() || ps.childNodes.length < 1){
16497                 return this.select(ps);
16498             } else{
16499                 var lc = ps.lastChild;
16500                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16501                     lc = lc.lastChild;
16502                 }
16503                 return this.select(lc);
16504             }
16505         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16506             return this.select(s.parentNode);
16507         }
16508         return null;
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     selectNext : function(){
16516         var s = this.selNode || this.lastSelNode;
16517         if(!s){
16518             return null;
16519         }
16520         if(s.firstChild && s.isExpanded()){
16521              return this.select(s.firstChild);
16522          }else if(s.nextSibling){
16523              return this.select(s.nextSibling);
16524          }else if(s.parentNode){
16525             var newS = null;
16526             s.parentNode.bubble(function(){
16527                 if(this.nextSibling){
16528                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16529                     return false;
16530                 }
16531             });
16532             return newS;
16533          }
16534         return null;
16535     },
16536
16537     onKeyDown : function(e){
16538         var s = this.selNode || this.lastSelNode;
16539         // undesirable, but required
16540         var sm = this;
16541         if(!s){
16542             return;
16543         }
16544         var k = e.getKey();
16545         switch(k){
16546              case e.DOWN:
16547                  e.stopEvent();
16548                  this.selectNext();
16549              break;
16550              case e.UP:
16551                  e.stopEvent();
16552                  this.selectPrevious();
16553              break;
16554              case e.RIGHT:
16555                  e.preventDefault();
16556                  if(s.hasChildNodes()){
16557                      if(!s.isExpanded()){
16558                          s.expand();
16559                      }else if(s.firstChild){
16560                          this.select(s.firstChild, e);
16561                      }
16562                  }
16563              break;
16564              case e.LEFT:
16565                  e.preventDefault();
16566                  if(s.hasChildNodes() && s.isExpanded()){
16567                      s.collapse();
16568                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16569                      this.select(s.parentNode, e);
16570                  }
16571              break;
16572         };
16573     }
16574 });
16575
16576 /**
16577  * @class Roo.tree.MultiSelectionModel
16578  * @extends Roo.util.Observable
16579  * Multi selection for a TreePanel.
16580  * @param {Object} cfg Configuration
16581  */
16582 Roo.tree.MultiSelectionModel = function(){
16583    this.selNodes = [];
16584    this.selMap = {};
16585    this.addEvents({
16586        /**
16587         * @event selectionchange
16588         * Fires when the selected nodes change
16589         * @param {MultiSelectionModel} this
16590         * @param {Array} nodes Array of the selected nodes
16591         */
16592        "selectionchange" : true
16593    });
16594    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16595    
16596 };
16597
16598 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16599     init : function(tree){
16600         this.tree = tree;
16601         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16602         tree.on("click", this.onNodeClick, this);
16603     },
16604     
16605     onNodeClick : function(node, e){
16606         this.select(node, e, e.ctrlKey);
16607     },
16608     
16609     /**
16610      * Select a node.
16611      * @param {TreeNode} node The node to select
16612      * @param {EventObject} e (optional) An event associated with the selection
16613      * @param {Boolean} keepExisting True to retain existing selections
16614      * @return {TreeNode} The selected node
16615      */
16616     select : function(node, e, keepExisting){
16617         if(keepExisting !== true){
16618             this.clearSelections(true);
16619         }
16620         if(this.isSelected(node)){
16621             this.lastSelNode = node;
16622             return node;
16623         }
16624         this.selNodes.push(node);
16625         this.selMap[node.id] = node;
16626         this.lastSelNode = node;
16627         node.ui.onSelectedChange(true);
16628         this.fireEvent("selectionchange", this, this.selNodes);
16629         return node;
16630     },
16631     
16632     /**
16633      * Deselect a node.
16634      * @param {TreeNode} node The node to unselect
16635      */
16636     unselect : function(node){
16637         if(this.selMap[node.id]){
16638             node.ui.onSelectedChange(false);
16639             var sn = this.selNodes;
16640             var index = -1;
16641             if(sn.indexOf){
16642                 index = sn.indexOf(node);
16643             }else{
16644                 for(var i = 0, len = sn.length; i < len; i++){
16645                     if(sn[i] == node){
16646                         index = i;
16647                         break;
16648                     }
16649                 }
16650             }
16651             if(index != -1){
16652                 this.selNodes.splice(index, 1);
16653             }
16654             delete this.selMap[node.id];
16655             this.fireEvent("selectionchange", this, this.selNodes);
16656         }
16657     },
16658     
16659     /**
16660      * Clear all selections
16661      */
16662     clearSelections : function(suppressEvent){
16663         var sn = this.selNodes;
16664         if(sn.length > 0){
16665             for(var i = 0, len = sn.length; i < len; i++){
16666                 sn[i].ui.onSelectedChange(false);
16667             }
16668             this.selNodes = [];
16669             this.selMap = {};
16670             if(suppressEvent !== true){
16671                 this.fireEvent("selectionchange", this, this.selNodes);
16672             }
16673         }
16674     },
16675     
16676     /**
16677      * Returns true if the node is selected
16678      * @param {TreeNode} node The node to check
16679      * @return {Boolean}
16680      */
16681     isSelected : function(node){
16682         return this.selMap[node.id] ? true : false;  
16683     },
16684     
16685     /**
16686      * Returns an array of the selected nodes
16687      * @return {Array}
16688      */
16689     getSelectedNodes : function(){
16690         return this.selNodes;    
16691     },
16692
16693     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16694
16695     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16696
16697     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16698 });/*
16699  * Based on:
16700  * Ext JS Library 1.1.1
16701  * Copyright(c) 2006-2007, Ext JS, LLC.
16702  *
16703  * Originally Released Under LGPL - original licence link has changed is not relivant.
16704  *
16705  * Fork - LGPL
16706  * <script type="text/javascript">
16707  */
16708  
16709 /**
16710  * @class Roo.tree.TreeNode
16711  * @extends Roo.data.Node
16712  * @cfg {String} text The text for this node
16713  * @cfg {Boolean} expanded true to start the node expanded
16714  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16715  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16716  * @cfg {Boolean} disabled true to start the node disabled
16717  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16718  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16719  * @cfg {String} cls A css class to be added to the node
16720  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16721  * @cfg {String} href URL of the link used for the node (defaults to #)
16722  * @cfg {String} hrefTarget target frame for the link
16723  * @cfg {String} qtip An Ext QuickTip for the node
16724  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16725  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16726  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16727  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16728  * (defaults to undefined with no checkbox rendered)
16729  * @constructor
16730  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16731  */
16732 Roo.tree.TreeNode = function(attributes){
16733     attributes = attributes || {};
16734     if(typeof attributes == "string"){
16735         attributes = {text: attributes};
16736     }
16737     this.childrenRendered = false;
16738     this.rendered = false;
16739     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16740     this.expanded = attributes.expanded === true;
16741     this.isTarget = attributes.isTarget !== false;
16742     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16743     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16744
16745     /**
16746      * Read-only. The text for this node. To change it use setText().
16747      * @type String
16748      */
16749     this.text = attributes.text;
16750     /**
16751      * True if this node is disabled.
16752      * @type Boolean
16753      */
16754     this.disabled = attributes.disabled === true;
16755
16756     this.addEvents({
16757         /**
16758         * @event textchange
16759         * Fires when the text for this node is changed
16760         * @param {Node} this This node
16761         * @param {String} text The new text
16762         * @param {String} oldText The old text
16763         */
16764         "textchange" : true,
16765         /**
16766         * @event beforeexpand
16767         * Fires before this node is expanded, return false to cancel.
16768         * @param {Node} this This node
16769         * @param {Boolean} deep
16770         * @param {Boolean} anim
16771         */
16772         "beforeexpand" : true,
16773         /**
16774         * @event beforecollapse
16775         * Fires before this node is collapsed, return false to cancel.
16776         * @param {Node} this This node
16777         * @param {Boolean} deep
16778         * @param {Boolean} anim
16779         */
16780         "beforecollapse" : true,
16781         /**
16782         * @event expand
16783         * Fires when this node is expanded
16784         * @param {Node} this This node
16785         */
16786         "expand" : true,
16787         /**
16788         * @event disabledchange
16789         * Fires when the disabled status of this node changes
16790         * @param {Node} this This node
16791         * @param {Boolean} disabled
16792         */
16793         "disabledchange" : true,
16794         /**
16795         * @event collapse
16796         * Fires when this node is collapsed
16797         * @param {Node} this This node
16798         */
16799         "collapse" : true,
16800         /**
16801         * @event beforeclick
16802         * Fires before click processing. Return false to cancel the default action.
16803         * @param {Node} this This node
16804         * @param {Roo.EventObject} e The event object
16805         */
16806         "beforeclick":true,
16807         /**
16808         * @event checkchange
16809         * Fires when a node with a checkbox's checked property changes
16810         * @param {Node} this This node
16811         * @param {Boolean} checked
16812         */
16813         "checkchange":true,
16814         /**
16815         * @event click
16816         * Fires when this node is clicked
16817         * @param {Node} this This node
16818         * @param {Roo.EventObject} e The event object
16819         */
16820         "click":true,
16821         /**
16822         * @event dblclick
16823         * Fires when this node is double clicked
16824         * @param {Node} this This node
16825         * @param {Roo.EventObject} e The event object
16826         */
16827         "dblclick":true,
16828         /**
16829         * @event contextmenu
16830         * Fires when this node is right clicked
16831         * @param {Node} this This node
16832         * @param {Roo.EventObject} e The event object
16833         */
16834         "contextmenu":true,
16835         /**
16836         * @event beforechildrenrendered
16837         * Fires right before the child nodes for this node are rendered
16838         * @param {Node} this This node
16839         */
16840         "beforechildrenrendered":true
16841     });
16842
16843     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16844
16845     /**
16846      * Read-only. The UI for this node
16847      * @type TreeNodeUI
16848      */
16849     this.ui = new uiClass(this);
16850     
16851     // finally support items[]
16852     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16853         return;
16854     }
16855     
16856     
16857     Roo.each(this.attributes.items, function(c) {
16858         this.appendChild(Roo.factory(c,Roo.Tree));
16859     }, this);
16860     delete this.attributes.items;
16861     
16862     
16863     
16864 };
16865 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16866     preventHScroll: true,
16867     /**
16868      * Returns true if this node is expanded
16869      * @return {Boolean}
16870      */
16871     isExpanded : function(){
16872         return this.expanded;
16873     },
16874
16875     /**
16876      * Returns the UI object for this node
16877      * @return {TreeNodeUI}
16878      */
16879     getUI : function(){
16880         return this.ui;
16881     },
16882
16883     // private override
16884     setFirstChild : function(node){
16885         var of = this.firstChild;
16886         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16887         if(this.childrenRendered && of && node != of){
16888             of.renderIndent(true, true);
16889         }
16890         if(this.rendered){
16891             this.renderIndent(true, true);
16892         }
16893     },
16894
16895     // private override
16896     setLastChild : function(node){
16897         var ol = this.lastChild;
16898         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16899         if(this.childrenRendered && ol && node != ol){
16900             ol.renderIndent(true, true);
16901         }
16902         if(this.rendered){
16903             this.renderIndent(true, true);
16904         }
16905     },
16906
16907     // these methods are overridden to provide lazy rendering support
16908     // private override
16909     appendChild : function()
16910     {
16911         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16912         if(node && this.childrenRendered){
16913             node.render();
16914         }
16915         this.ui.updateExpandIcon();
16916         return node;
16917     },
16918
16919     // private override
16920     removeChild : function(node){
16921         this.ownerTree.getSelectionModel().unselect(node);
16922         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16923         // if it's been rendered remove dom node
16924         if(this.childrenRendered){
16925             node.ui.remove();
16926         }
16927         if(this.childNodes.length < 1){
16928             this.collapse(false, false);
16929         }else{
16930             this.ui.updateExpandIcon();
16931         }
16932         if(!this.firstChild) {
16933             this.childrenRendered = false;
16934         }
16935         return node;
16936     },
16937
16938     // private override
16939     insertBefore : function(node, refNode){
16940         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16941         if(newNode && refNode && this.childrenRendered){
16942             node.render();
16943         }
16944         this.ui.updateExpandIcon();
16945         return newNode;
16946     },
16947
16948     /**
16949      * Sets the text for this node
16950      * @param {String} text
16951      */
16952     setText : function(text){
16953         var oldText = this.text;
16954         this.text = text;
16955         this.attributes.text = text;
16956         if(this.rendered){ // event without subscribing
16957             this.ui.onTextChange(this, text, oldText);
16958         }
16959         this.fireEvent("textchange", this, text, oldText);
16960     },
16961
16962     /**
16963      * Triggers selection of this node
16964      */
16965     select : function(){
16966         this.getOwnerTree().getSelectionModel().select(this);
16967     },
16968
16969     /**
16970      * Triggers deselection of this node
16971      */
16972     unselect : function(){
16973         this.getOwnerTree().getSelectionModel().unselect(this);
16974     },
16975
16976     /**
16977      * Returns true if this node is selected
16978      * @return {Boolean}
16979      */
16980     isSelected : function(){
16981         return this.getOwnerTree().getSelectionModel().isSelected(this);
16982     },
16983
16984     /**
16985      * Expand this node.
16986      * @param {Boolean} deep (optional) True to expand all children as well
16987      * @param {Boolean} anim (optional) false to cancel the default animation
16988      * @param {Function} callback (optional) A callback to be called when
16989      * expanding this node completes (does not wait for deep expand to complete).
16990      * Called with 1 parameter, this node.
16991      */
16992     expand : function(deep, anim, callback){
16993         if(!this.expanded){
16994             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
16995                 return;
16996             }
16997             if(!this.childrenRendered){
16998                 this.renderChildren();
16999             }
17000             this.expanded = true;
17001             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17002                 this.ui.animExpand(function(){
17003                     this.fireEvent("expand", this);
17004                     if(typeof callback == "function"){
17005                         callback(this);
17006                     }
17007                     if(deep === true){
17008                         this.expandChildNodes(true);
17009                     }
17010                 }.createDelegate(this));
17011                 return;
17012             }else{
17013                 this.ui.expand();
17014                 this.fireEvent("expand", this);
17015                 if(typeof callback == "function"){
17016                     callback(this);
17017                 }
17018             }
17019         }else{
17020            if(typeof callback == "function"){
17021                callback(this);
17022            }
17023         }
17024         if(deep === true){
17025             this.expandChildNodes(true);
17026         }
17027     },
17028
17029     isHiddenRoot : function(){
17030         return this.isRoot && !this.getOwnerTree().rootVisible;
17031     },
17032
17033     /**
17034      * Collapse this node.
17035      * @param {Boolean} deep (optional) True to collapse all children as well
17036      * @param {Boolean} anim (optional) false to cancel the default animation
17037      */
17038     collapse : function(deep, anim){
17039         if(this.expanded && !this.isHiddenRoot()){
17040             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17041                 return;
17042             }
17043             this.expanded = false;
17044             if((this.getOwnerTree().animate && anim !== false) || anim){
17045                 this.ui.animCollapse(function(){
17046                     this.fireEvent("collapse", this);
17047                     if(deep === true){
17048                         this.collapseChildNodes(true);
17049                     }
17050                 }.createDelegate(this));
17051                 return;
17052             }else{
17053                 this.ui.collapse();
17054                 this.fireEvent("collapse", this);
17055             }
17056         }
17057         if(deep === true){
17058             var cs = this.childNodes;
17059             for(var i = 0, len = cs.length; i < len; i++) {
17060                 cs[i].collapse(true, false);
17061             }
17062         }
17063     },
17064
17065     // private
17066     delayedExpand : function(delay){
17067         if(!this.expandProcId){
17068             this.expandProcId = this.expand.defer(delay, this);
17069         }
17070     },
17071
17072     // private
17073     cancelExpand : function(){
17074         if(this.expandProcId){
17075             clearTimeout(this.expandProcId);
17076         }
17077         this.expandProcId = false;
17078     },
17079
17080     /**
17081      * Toggles expanded/collapsed state of the node
17082      */
17083     toggle : function(){
17084         if(this.expanded){
17085             this.collapse();
17086         }else{
17087             this.expand();
17088         }
17089     },
17090
17091     /**
17092      * Ensures all parent nodes are expanded
17093      */
17094     ensureVisible : function(callback){
17095         var tree = this.getOwnerTree();
17096         tree.expandPath(this.parentNode.getPath(), false, function(){
17097             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17098             Roo.callback(callback);
17099         }.createDelegate(this));
17100     },
17101
17102     /**
17103      * Expand all child nodes
17104      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17105      */
17106     expandChildNodes : function(deep){
17107         var cs = this.childNodes;
17108         for(var i = 0, len = cs.length; i < len; i++) {
17109                 cs[i].expand(deep);
17110         }
17111     },
17112
17113     /**
17114      * Collapse all child nodes
17115      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17116      */
17117     collapseChildNodes : function(deep){
17118         var cs = this.childNodes;
17119         for(var i = 0, len = cs.length; i < len; i++) {
17120                 cs[i].collapse(deep);
17121         }
17122     },
17123
17124     /**
17125      * Disables this node
17126      */
17127     disable : function(){
17128         this.disabled = true;
17129         this.unselect();
17130         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17131             this.ui.onDisableChange(this, true);
17132         }
17133         this.fireEvent("disabledchange", this, true);
17134     },
17135
17136     /**
17137      * Enables this node
17138      */
17139     enable : function(){
17140         this.disabled = false;
17141         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17142             this.ui.onDisableChange(this, false);
17143         }
17144         this.fireEvent("disabledchange", this, false);
17145     },
17146
17147     // private
17148     renderChildren : function(suppressEvent){
17149         if(suppressEvent !== false){
17150             this.fireEvent("beforechildrenrendered", this);
17151         }
17152         var cs = this.childNodes;
17153         for(var i = 0, len = cs.length; i < len; i++){
17154             cs[i].render(true);
17155         }
17156         this.childrenRendered = true;
17157     },
17158
17159     // private
17160     sort : function(fn, scope){
17161         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17162         if(this.childrenRendered){
17163             var cs = this.childNodes;
17164             for(var i = 0, len = cs.length; i < len; i++){
17165                 cs[i].render(true);
17166             }
17167         }
17168     },
17169
17170     // private
17171     render : function(bulkRender){
17172         this.ui.render(bulkRender);
17173         if(!this.rendered){
17174             this.rendered = true;
17175             if(this.expanded){
17176                 this.expanded = false;
17177                 this.expand(false, false);
17178             }
17179         }
17180     },
17181
17182     // private
17183     renderIndent : function(deep, refresh){
17184         if(refresh){
17185             this.ui.childIndent = null;
17186         }
17187         this.ui.renderIndent();
17188         if(deep === true && this.childrenRendered){
17189             var cs = this.childNodes;
17190             for(var i = 0, len = cs.length; i < len; i++){
17191                 cs[i].renderIndent(true, refresh);
17192             }
17193         }
17194     }
17195 });/*
17196  * Based on:
17197  * Ext JS Library 1.1.1
17198  * Copyright(c) 2006-2007, Ext JS, LLC.
17199  *
17200  * Originally Released Under LGPL - original licence link has changed is not relivant.
17201  *
17202  * Fork - LGPL
17203  * <script type="text/javascript">
17204  */
17205  
17206 /**
17207  * @class Roo.tree.AsyncTreeNode
17208  * @extends Roo.tree.TreeNode
17209  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17210  * @constructor
17211  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17212  */
17213  Roo.tree.AsyncTreeNode = function(config){
17214     this.loaded = false;
17215     this.loading = false;
17216     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17217     /**
17218     * @event beforeload
17219     * Fires before this node is loaded, return false to cancel
17220     * @param {Node} this This node
17221     */
17222     this.addEvents({'beforeload':true, 'load': true});
17223     /**
17224     * @event load
17225     * Fires when this node is loaded
17226     * @param {Node} this This node
17227     */
17228     /**
17229      * The loader used by this node (defaults to using the tree's defined loader)
17230      * @type TreeLoader
17231      * @property loader
17232      */
17233 };
17234 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17235     expand : function(deep, anim, callback){
17236         if(this.loading){ // if an async load is already running, waiting til it's done
17237             var timer;
17238             var f = function(){
17239                 if(!this.loading){ // done loading
17240                     clearInterval(timer);
17241                     this.expand(deep, anim, callback);
17242                 }
17243             }.createDelegate(this);
17244             timer = setInterval(f, 200);
17245             return;
17246         }
17247         if(!this.loaded){
17248             if(this.fireEvent("beforeload", this) === false){
17249                 return;
17250             }
17251             this.loading = true;
17252             this.ui.beforeLoad(this);
17253             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17254             if(loader){
17255                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17256                 return;
17257             }
17258         }
17259         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17260     },
17261     
17262     /**
17263      * Returns true if this node is currently loading
17264      * @return {Boolean}
17265      */
17266     isLoading : function(){
17267         return this.loading;  
17268     },
17269     
17270     loadComplete : function(deep, anim, callback){
17271         this.loading = false;
17272         this.loaded = true;
17273         this.ui.afterLoad(this);
17274         this.fireEvent("load", this);
17275         this.expand(deep, anim, callback);
17276     },
17277     
17278     /**
17279      * Returns true if this node has been loaded
17280      * @return {Boolean}
17281      */
17282     isLoaded : function(){
17283         return this.loaded;
17284     },
17285     
17286     hasChildNodes : function(){
17287         if(!this.isLeaf() && !this.loaded){
17288             return true;
17289         }else{
17290             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17291         }
17292     },
17293
17294     /**
17295      * Trigger a reload for this node
17296      * @param {Function} callback
17297      */
17298     reload : function(callback){
17299         this.collapse(false, false);
17300         while(this.firstChild){
17301             this.removeChild(this.firstChild);
17302         }
17303         this.childrenRendered = false;
17304         this.loaded = false;
17305         if(this.isHiddenRoot()){
17306             this.expanded = false;
17307         }
17308         this.expand(false, false, callback);
17309     }
17310 });/*
17311  * Based on:
17312  * Ext JS Library 1.1.1
17313  * Copyright(c) 2006-2007, Ext JS, LLC.
17314  *
17315  * Originally Released Under LGPL - original licence link has changed is not relivant.
17316  *
17317  * Fork - LGPL
17318  * <script type="text/javascript">
17319  */
17320  
17321 /**
17322  * @class Roo.tree.TreeNodeUI
17323  * @constructor
17324  * @param {Object} node The node to render
17325  * The TreeNode UI implementation is separate from the
17326  * tree implementation. Unless you are customizing the tree UI,
17327  * you should never have to use this directly.
17328  */
17329 Roo.tree.TreeNodeUI = function(node){
17330     this.node = node;
17331     this.rendered = false;
17332     this.animating = false;
17333     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17334 };
17335
17336 Roo.tree.TreeNodeUI.prototype = {
17337     removeChild : function(node){
17338         if(this.rendered){
17339             this.ctNode.removeChild(node.ui.getEl());
17340         }
17341     },
17342
17343     beforeLoad : function(){
17344          this.addClass("x-tree-node-loading");
17345     },
17346
17347     afterLoad : function(){
17348          this.removeClass("x-tree-node-loading");
17349     },
17350
17351     onTextChange : function(node, text, oldText){
17352         if(this.rendered){
17353             this.textNode.innerHTML = text;
17354         }
17355     },
17356
17357     onDisableChange : function(node, state){
17358         this.disabled = state;
17359         if(state){
17360             this.addClass("x-tree-node-disabled");
17361         }else{
17362             this.removeClass("x-tree-node-disabled");
17363         }
17364     },
17365
17366     onSelectedChange : function(state){
17367         if(state){
17368             this.focus();
17369             this.addClass("x-tree-selected");
17370         }else{
17371             //this.blur();
17372             this.removeClass("x-tree-selected");
17373         }
17374     },
17375
17376     onMove : function(tree, node, oldParent, newParent, index, refNode){
17377         this.childIndent = null;
17378         if(this.rendered){
17379             var targetNode = newParent.ui.getContainer();
17380             if(!targetNode){//target not rendered
17381                 this.holder = document.createElement("div");
17382                 this.holder.appendChild(this.wrap);
17383                 return;
17384             }
17385             var insertBefore = refNode ? refNode.ui.getEl() : null;
17386             if(insertBefore){
17387                 targetNode.insertBefore(this.wrap, insertBefore);
17388             }else{
17389                 targetNode.appendChild(this.wrap);
17390             }
17391             this.node.renderIndent(true);
17392         }
17393     },
17394
17395     addClass : function(cls){
17396         if(this.elNode){
17397             Roo.fly(this.elNode).addClass(cls);
17398         }
17399     },
17400
17401     removeClass : function(cls){
17402         if(this.elNode){
17403             Roo.fly(this.elNode).removeClass(cls);
17404         }
17405     },
17406
17407     remove : function(){
17408         if(this.rendered){
17409             this.holder = document.createElement("div");
17410             this.holder.appendChild(this.wrap);
17411         }
17412     },
17413
17414     fireEvent : function(){
17415         return this.node.fireEvent.apply(this.node, arguments);
17416     },
17417
17418     initEvents : function(){
17419         this.node.on("move", this.onMove, this);
17420         var E = Roo.EventManager;
17421         var a = this.anchor;
17422
17423         var el = Roo.fly(a, '_treeui');
17424
17425         if(Roo.isOpera){ // opera render bug ignores the CSS
17426             el.setStyle("text-decoration", "none");
17427         }
17428
17429         el.on("click", this.onClick, this);
17430         el.on("dblclick", this.onDblClick, this);
17431
17432         if(this.checkbox){
17433             Roo.EventManager.on(this.checkbox,
17434                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17435         }
17436
17437         el.on("contextmenu", this.onContextMenu, this);
17438
17439         var icon = Roo.fly(this.iconNode);
17440         icon.on("click", this.onClick, this);
17441         icon.on("dblclick", this.onDblClick, this);
17442         icon.on("contextmenu", this.onContextMenu, this);
17443         E.on(this.ecNode, "click", this.ecClick, this, true);
17444
17445         if(this.node.disabled){
17446             this.addClass("x-tree-node-disabled");
17447         }
17448         if(this.node.hidden){
17449             this.addClass("x-tree-node-disabled");
17450         }
17451         var ot = this.node.getOwnerTree();
17452         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17453         if(dd && (!this.node.isRoot || ot.rootVisible)){
17454             Roo.dd.Registry.register(this.elNode, {
17455                 node: this.node,
17456                 handles: this.getDDHandles(),
17457                 isHandle: false
17458             });
17459         }
17460     },
17461
17462     getDDHandles : function(){
17463         return [this.iconNode, this.textNode];
17464     },
17465
17466     hide : function(){
17467         if(this.rendered){
17468             this.wrap.style.display = "none";
17469         }
17470     },
17471
17472     show : function(){
17473         if(this.rendered){
17474             this.wrap.style.display = "";
17475         }
17476     },
17477
17478     onContextMenu : function(e){
17479         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17480             e.preventDefault();
17481             this.focus();
17482             this.fireEvent("contextmenu", this.node, e);
17483         }
17484     },
17485
17486     onClick : function(e){
17487         if(this.dropping){
17488             e.stopEvent();
17489             return;
17490         }
17491         if(this.fireEvent("beforeclick", this.node, e) !== false){
17492             if(!this.disabled && this.node.attributes.href){
17493                 this.fireEvent("click", this.node, e);
17494                 return;
17495             }
17496             e.preventDefault();
17497             if(this.disabled){
17498                 return;
17499             }
17500
17501             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17502                 this.node.toggle();
17503             }
17504
17505             this.fireEvent("click", this.node, e);
17506         }else{
17507             e.stopEvent();
17508         }
17509     },
17510
17511     onDblClick : function(e){
17512         e.preventDefault();
17513         if(this.disabled){
17514             return;
17515         }
17516         if(this.checkbox){
17517             this.toggleCheck();
17518         }
17519         if(!this.animating && this.node.hasChildNodes()){
17520             this.node.toggle();
17521         }
17522         this.fireEvent("dblclick", this.node, e);
17523     },
17524
17525     onCheckChange : function(){
17526         var checked = this.checkbox.checked;
17527         this.node.attributes.checked = checked;
17528         this.fireEvent('checkchange', this.node, checked);
17529     },
17530
17531     ecClick : function(e){
17532         if(!this.animating && this.node.hasChildNodes()){
17533             this.node.toggle();
17534         }
17535     },
17536
17537     startDrop : function(){
17538         this.dropping = true;
17539     },
17540
17541     // delayed drop so the click event doesn't get fired on a drop
17542     endDrop : function(){
17543        setTimeout(function(){
17544            this.dropping = false;
17545        }.createDelegate(this), 50);
17546     },
17547
17548     expand : function(){
17549         this.updateExpandIcon();
17550         this.ctNode.style.display = "";
17551     },
17552
17553     focus : function(){
17554         if(!this.node.preventHScroll){
17555             try{this.anchor.focus();
17556             }catch(e){}
17557         }else if(!Roo.isIE){
17558             try{
17559                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17560                 var l = noscroll.scrollLeft;
17561                 this.anchor.focus();
17562                 noscroll.scrollLeft = l;
17563             }catch(e){}
17564         }
17565     },
17566
17567     toggleCheck : function(value){
17568         var cb = this.checkbox;
17569         if(cb){
17570             cb.checked = (value === undefined ? !cb.checked : value);
17571         }
17572     },
17573
17574     blur : function(){
17575         try{
17576             this.anchor.blur();
17577         }catch(e){}
17578     },
17579
17580     animExpand : function(callback){
17581         var ct = Roo.get(this.ctNode);
17582         ct.stopFx();
17583         if(!this.node.hasChildNodes()){
17584             this.updateExpandIcon();
17585             this.ctNode.style.display = "";
17586             Roo.callback(callback);
17587             return;
17588         }
17589         this.animating = true;
17590         this.updateExpandIcon();
17591
17592         ct.slideIn('t', {
17593            callback : function(){
17594                this.animating = false;
17595                Roo.callback(callback);
17596             },
17597             scope: this,
17598             duration: this.node.ownerTree.duration || .25
17599         });
17600     },
17601
17602     highlight : function(){
17603         var tree = this.node.getOwnerTree();
17604         Roo.fly(this.wrap).highlight(
17605             tree.hlColor || "C3DAF9",
17606             {endColor: tree.hlBaseColor}
17607         );
17608     },
17609
17610     collapse : function(){
17611         this.updateExpandIcon();
17612         this.ctNode.style.display = "none";
17613     },
17614
17615     animCollapse : function(callback){
17616         var ct = Roo.get(this.ctNode);
17617         ct.enableDisplayMode('block');
17618         ct.stopFx();
17619
17620         this.animating = true;
17621         this.updateExpandIcon();
17622
17623         ct.slideOut('t', {
17624             callback : function(){
17625                this.animating = false;
17626                Roo.callback(callback);
17627             },
17628             scope: this,
17629             duration: this.node.ownerTree.duration || .25
17630         });
17631     },
17632
17633     getContainer : function(){
17634         return this.ctNode;
17635     },
17636
17637     getEl : function(){
17638         return this.wrap;
17639     },
17640
17641     appendDDGhost : function(ghostNode){
17642         ghostNode.appendChild(this.elNode.cloneNode(true));
17643     },
17644
17645     getDDRepairXY : function(){
17646         return Roo.lib.Dom.getXY(this.iconNode);
17647     },
17648
17649     onRender : function(){
17650         this.render();
17651     },
17652
17653     render : function(bulkRender){
17654         var n = this.node, a = n.attributes;
17655         var targetNode = n.parentNode ?
17656               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17657
17658         if(!this.rendered){
17659             this.rendered = true;
17660
17661             this.renderElements(n, a, targetNode, bulkRender);
17662
17663             if(a.qtip){
17664                if(this.textNode.setAttributeNS){
17665                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17666                    if(a.qtipTitle){
17667                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17668                    }
17669                }else{
17670                    this.textNode.setAttribute("ext:qtip", a.qtip);
17671                    if(a.qtipTitle){
17672                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17673                    }
17674                }
17675             }else if(a.qtipCfg){
17676                 a.qtipCfg.target = Roo.id(this.textNode);
17677                 Roo.QuickTips.register(a.qtipCfg);
17678             }
17679             this.initEvents();
17680             if(!this.node.expanded){
17681                 this.updateExpandIcon();
17682             }
17683         }else{
17684             if(bulkRender === true) {
17685                 targetNode.appendChild(this.wrap);
17686             }
17687         }
17688     },
17689
17690     renderElements : function(n, a, targetNode, bulkRender)
17691     {
17692         // add some indent caching, this helps performance when rendering a large tree
17693         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17694         var t = n.getOwnerTree();
17695         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17696         if (typeof(n.attributes.html) != 'undefined') {
17697             txt = n.attributes.html;
17698         }
17699         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17700         var cb = typeof a.checked == 'boolean';
17701         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17702         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17703             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17704             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17705             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17706             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17707             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17708              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17709                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17710             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17711             "</li>"];
17712
17713         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17714             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17715                                 n.nextSibling.ui.getEl(), buf.join(""));
17716         }else{
17717             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17718         }
17719
17720         this.elNode = this.wrap.childNodes[0];
17721         this.ctNode = this.wrap.childNodes[1];
17722         var cs = this.elNode.childNodes;
17723         this.indentNode = cs[0];
17724         this.ecNode = cs[1];
17725         this.iconNode = cs[2];
17726         var index = 3;
17727         if(cb){
17728             this.checkbox = cs[3];
17729             index++;
17730         }
17731         this.anchor = cs[index];
17732         this.textNode = cs[index].firstChild;
17733     },
17734
17735     getAnchor : function(){
17736         return this.anchor;
17737     },
17738
17739     getTextEl : function(){
17740         return this.textNode;
17741     },
17742
17743     getIconEl : function(){
17744         return this.iconNode;
17745     },
17746
17747     isChecked : function(){
17748         return this.checkbox ? this.checkbox.checked : false;
17749     },
17750
17751     updateExpandIcon : function(){
17752         if(this.rendered){
17753             var n = this.node, c1, c2;
17754             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17755             var hasChild = n.hasChildNodes();
17756             if(hasChild){
17757                 if(n.expanded){
17758                     cls += "-minus";
17759                     c1 = "x-tree-node-collapsed";
17760                     c2 = "x-tree-node-expanded";
17761                 }else{
17762                     cls += "-plus";
17763                     c1 = "x-tree-node-expanded";
17764                     c2 = "x-tree-node-collapsed";
17765                 }
17766                 if(this.wasLeaf){
17767                     this.removeClass("x-tree-node-leaf");
17768                     this.wasLeaf = false;
17769                 }
17770                 if(this.c1 != c1 || this.c2 != c2){
17771                     Roo.fly(this.elNode).replaceClass(c1, c2);
17772                     this.c1 = c1; this.c2 = c2;
17773                 }
17774             }else{
17775                 // this changes non-leafs into leafs if they have no children.
17776                 // it's not very rational behaviour..
17777                 
17778                 if(!this.wasLeaf && this.node.leaf){
17779                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17780                     delete this.c1;
17781                     delete this.c2;
17782                     this.wasLeaf = true;
17783                 }
17784             }
17785             var ecc = "x-tree-ec-icon "+cls;
17786             if(this.ecc != ecc){
17787                 this.ecNode.className = ecc;
17788                 this.ecc = ecc;
17789             }
17790         }
17791     },
17792
17793     getChildIndent : function(){
17794         if(!this.childIndent){
17795             var buf = [];
17796             var p = this.node;
17797             while(p){
17798                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17799                     if(!p.isLast()) {
17800                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17801                     } else {
17802                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17803                     }
17804                 }
17805                 p = p.parentNode;
17806             }
17807             this.childIndent = buf.join("");
17808         }
17809         return this.childIndent;
17810     },
17811
17812     renderIndent : function(){
17813         if(this.rendered){
17814             var indent = "";
17815             var p = this.node.parentNode;
17816             if(p){
17817                 indent = p.ui.getChildIndent();
17818             }
17819             if(this.indentMarkup != indent){ // don't rerender if not required
17820                 this.indentNode.innerHTML = indent;
17821                 this.indentMarkup = indent;
17822             }
17823             this.updateExpandIcon();
17824         }
17825     }
17826 };
17827
17828 Roo.tree.RootTreeNodeUI = function(){
17829     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17830 };
17831 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17832     render : function(){
17833         if(!this.rendered){
17834             var targetNode = this.node.ownerTree.innerCt.dom;
17835             this.node.expanded = true;
17836             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17837             this.wrap = this.ctNode = targetNode.firstChild;
17838         }
17839     },
17840     collapse : function(){
17841     },
17842     expand : function(){
17843     }
17844 });/*
17845  * Based on:
17846  * Ext JS Library 1.1.1
17847  * Copyright(c) 2006-2007, Ext JS, LLC.
17848  *
17849  * Originally Released Under LGPL - original licence link has changed is not relivant.
17850  *
17851  * Fork - LGPL
17852  * <script type="text/javascript">
17853  */
17854 /**
17855  * @class Roo.tree.TreeLoader
17856  * @extends Roo.util.Observable
17857  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17858  * nodes from a specified URL. The response must be a javascript Array definition
17859  * who's elements are node definition objects. eg:
17860  * <pre><code>
17861 {  success : true,
17862    data :      [
17863    
17864     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17865     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17866     ]
17867 }
17868
17869
17870 </code></pre>
17871  * <br><br>
17872  * The old style respose with just an array is still supported, but not recommended.
17873  * <br><br>
17874  *
17875  * A server request is sent, and child nodes are loaded only when a node is expanded.
17876  * The loading node's id is passed to the server under the parameter name "node" to
17877  * enable the server to produce the correct child nodes.
17878  * <br><br>
17879  * To pass extra parameters, an event handler may be attached to the "beforeload"
17880  * event, and the parameters specified in the TreeLoader's baseParams property:
17881  * <pre><code>
17882     myTreeLoader.on("beforeload", function(treeLoader, node) {
17883         this.baseParams.category = node.attributes.category;
17884     }, this);
17885 </code></pre><
17886  * This would pass an HTTP parameter called "category" to the server containing
17887  * the value of the Node's "category" attribute.
17888  * @constructor
17889  * Creates a new Treeloader.
17890  * @param {Object} config A config object containing config properties.
17891  */
17892 Roo.tree.TreeLoader = function(config){
17893     this.baseParams = {};
17894     this.requestMethod = "POST";
17895     Roo.apply(this, config);
17896
17897     this.addEvents({
17898     
17899         /**
17900          * @event beforeload
17901          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17902          * @param {Object} This TreeLoader object.
17903          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17904          * @param {Object} callback The callback function specified in the {@link #load} call.
17905          */
17906         beforeload : true,
17907         /**
17908          * @event load
17909          * Fires when the node has been successfuly loaded.
17910          * @param {Object} This TreeLoader object.
17911          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17912          * @param {Object} response The response object containing the data from the server.
17913          */
17914         load : true,
17915         /**
17916          * @event loadexception
17917          * Fires if the network request failed.
17918          * @param {Object} This TreeLoader object.
17919          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17920          * @param {Object} response The response object containing the data from the server.
17921          */
17922         loadexception : true,
17923         /**
17924          * @event create
17925          * Fires before a node is created, enabling you to return custom Node types 
17926          * @param {Object} This TreeLoader object.
17927          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17928          */
17929         create : true
17930     });
17931
17932     Roo.tree.TreeLoader.superclass.constructor.call(this);
17933 };
17934
17935 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17936     /**
17937     * @cfg {String} dataUrl The URL from which to request a Json string which
17938     * specifies an array of node definition object representing the child nodes
17939     * to be loaded.
17940     */
17941     /**
17942     * @cfg {String} requestMethod either GET or POST
17943     * defaults to POST (due to BC)
17944     * to be loaded.
17945     */
17946     /**
17947     * @cfg {Object} baseParams (optional) An object containing properties which
17948     * specify HTTP parameters to be passed to each request for child nodes.
17949     */
17950     /**
17951     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17952     * created by this loader. If the attributes sent by the server have an attribute in this object,
17953     * they take priority.
17954     */
17955     /**
17956     * @cfg {Object} uiProviders (optional) An object containing properties which
17957     * 
17958     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17959     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17960     * <i>uiProvider</i> attribute of a returned child node is a string rather
17961     * than a reference to a TreeNodeUI implementation, this that string value
17962     * is used as a property name in the uiProviders object. You can define the provider named
17963     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17964     */
17965     uiProviders : {},
17966
17967     /**
17968     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17969     * child nodes before loading.
17970     */
17971     clearOnLoad : true,
17972
17973     /**
17974     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17975     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17976     * Grid query { data : [ .....] }
17977     */
17978     
17979     root : false,
17980      /**
17981     * @cfg {String} queryParam (optional) 
17982     * Name of the query as it will be passed on the querystring (defaults to 'node')
17983     * eg. the request will be ?node=[id]
17984     */
17985     
17986     
17987     queryParam: false,
17988     
17989     /**
17990      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17991      * This is called automatically when a node is expanded, but may be used to reload
17992      * a node (or append new children if the {@link #clearOnLoad} option is false.)
17993      * @param {Roo.tree.TreeNode} node
17994      * @param {Function} callback
17995      */
17996     load : function(node, callback){
17997         if(this.clearOnLoad){
17998             while(node.firstChild){
17999                 node.removeChild(node.firstChild);
18000             }
18001         }
18002         if(node.attributes.children){ // preloaded json children
18003             var cs = node.attributes.children;
18004             for(var i = 0, len = cs.length; i < len; i++){
18005                 node.appendChild(this.createNode(cs[i]));
18006             }
18007             if(typeof callback == "function"){
18008                 callback();
18009             }
18010         }else if(this.dataUrl){
18011             this.requestData(node, callback);
18012         }
18013     },
18014
18015     getParams: function(node){
18016         var buf = [], bp = this.baseParams;
18017         for(var key in bp){
18018             if(typeof bp[key] != "function"){
18019                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18020             }
18021         }
18022         var n = this.queryParam === false ? 'node' : this.queryParam;
18023         buf.push(n + "=", encodeURIComponent(node.id));
18024         return buf.join("");
18025     },
18026
18027     requestData : function(node, callback){
18028         if(this.fireEvent("beforeload", this, node, callback) !== false){
18029             this.transId = Roo.Ajax.request({
18030                 method:this.requestMethod,
18031                 url: this.dataUrl||this.url,
18032                 success: this.handleResponse,
18033                 failure: this.handleFailure,
18034                 scope: this,
18035                 argument: {callback: callback, node: node},
18036                 params: this.getParams(node)
18037             });
18038         }else{
18039             // if the load is cancelled, make sure we notify
18040             // the node that we are done
18041             if(typeof callback == "function"){
18042                 callback();
18043             }
18044         }
18045     },
18046
18047     isLoading : function(){
18048         return this.transId ? true : false;
18049     },
18050
18051     abort : function(){
18052         if(this.isLoading()){
18053             Roo.Ajax.abort(this.transId);
18054         }
18055     },
18056
18057     // private
18058     createNode : function(attr)
18059     {
18060         // apply baseAttrs, nice idea Corey!
18061         if(this.baseAttrs){
18062             Roo.applyIf(attr, this.baseAttrs);
18063         }
18064         if(this.applyLoader !== false){
18065             attr.loader = this;
18066         }
18067         // uiProvider = depreciated..
18068         
18069         if(typeof(attr.uiProvider) == 'string'){
18070            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18071                 /**  eval:var:attr */ eval(attr.uiProvider);
18072         }
18073         if(typeof(this.uiProviders['default']) != 'undefined') {
18074             attr.uiProvider = this.uiProviders['default'];
18075         }
18076         
18077         this.fireEvent('create', this, attr);
18078         
18079         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18080         return(attr.leaf ?
18081                         new Roo.tree.TreeNode(attr) :
18082                         new Roo.tree.AsyncTreeNode(attr));
18083     },
18084
18085     processResponse : function(response, node, callback)
18086     {
18087         var json = response.responseText;
18088         try {
18089             
18090             var o = Roo.decode(json);
18091             
18092             if (this.root === false && typeof(o.success) != undefined) {
18093                 this.root = 'data'; // the default behaviour for list like data..
18094                 }
18095                 
18096             if (this.root !== false &&  !o.success) {
18097                 // it's a failure condition.
18098                 var a = response.argument;
18099                 this.fireEvent("loadexception", this, a.node, response);
18100                 Roo.log("Load failed - should have a handler really");
18101                 return;
18102             }
18103             
18104             
18105             
18106             if (this.root !== false) {
18107                  o = o[this.root];
18108             }
18109             
18110             for(var i = 0, len = o.length; i < len; i++){
18111                 var n = this.createNode(o[i]);
18112                 if(n){
18113                     node.appendChild(n);
18114                 }
18115             }
18116             if(typeof callback == "function"){
18117                 callback(this, node);
18118             }
18119         }catch(e){
18120             this.handleFailure(response);
18121         }
18122     },
18123
18124     handleResponse : function(response){
18125         this.transId = false;
18126         var a = response.argument;
18127         this.processResponse(response, a.node, a.callback);
18128         this.fireEvent("load", this, a.node, response);
18129     },
18130
18131     handleFailure : function(response)
18132     {
18133         // should handle failure better..
18134         this.transId = false;
18135         var a = response.argument;
18136         this.fireEvent("loadexception", this, a.node, response);
18137         if(typeof a.callback == "function"){
18138             a.callback(this, a.node);
18139         }
18140     }
18141 });/*
18142  * Based on:
18143  * Ext JS Library 1.1.1
18144  * Copyright(c) 2006-2007, Ext JS, LLC.
18145  *
18146  * Originally Released Under LGPL - original licence link has changed is not relivant.
18147  *
18148  * Fork - LGPL
18149  * <script type="text/javascript">
18150  */
18151
18152 /**
18153 * @class Roo.tree.TreeFilter
18154 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18155 * @param {TreePanel} tree
18156 * @param {Object} config (optional)
18157  */
18158 Roo.tree.TreeFilter = function(tree, config){
18159     this.tree = tree;
18160     this.filtered = {};
18161     Roo.apply(this, config);
18162 };
18163
18164 Roo.tree.TreeFilter.prototype = {
18165     clearBlank:false,
18166     reverse:false,
18167     autoClear:false,
18168     remove:false,
18169
18170      /**
18171      * Filter the data by a specific attribute.
18172      * @param {String/RegExp} value Either string that the attribute value
18173      * should start with or a RegExp to test against the attribute
18174      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18175      * @param {TreeNode} startNode (optional) The node to start the filter at.
18176      */
18177     filter : function(value, attr, startNode){
18178         attr = attr || "text";
18179         var f;
18180         if(typeof value == "string"){
18181             var vlen = value.length;
18182             // auto clear empty filter
18183             if(vlen == 0 && this.clearBlank){
18184                 this.clear();
18185                 return;
18186             }
18187             value = value.toLowerCase();
18188             f = function(n){
18189                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18190             };
18191         }else if(value.exec){ // regex?
18192             f = function(n){
18193                 return value.test(n.attributes[attr]);
18194             };
18195         }else{
18196             throw 'Illegal filter type, must be string or regex';
18197         }
18198         this.filterBy(f, null, startNode);
18199         },
18200
18201     /**
18202      * Filter by a function. The passed function will be called with each
18203      * node in the tree (or from the startNode). If the function returns true, the node is kept
18204      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18205      * @param {Function} fn The filter function
18206      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18207      */
18208     filterBy : function(fn, scope, startNode){
18209         startNode = startNode || this.tree.root;
18210         if(this.autoClear){
18211             this.clear();
18212         }
18213         var af = this.filtered, rv = this.reverse;
18214         var f = function(n){
18215             if(n == startNode){
18216                 return true;
18217             }
18218             if(af[n.id]){
18219                 return false;
18220             }
18221             var m = fn.call(scope || n, n);
18222             if(!m || rv){
18223                 af[n.id] = n;
18224                 n.ui.hide();
18225                 return false;
18226             }
18227             return true;
18228         };
18229         startNode.cascade(f);
18230         if(this.remove){
18231            for(var id in af){
18232                if(typeof id != "function"){
18233                    var n = af[id];
18234                    if(n && n.parentNode){
18235                        n.parentNode.removeChild(n);
18236                    }
18237                }
18238            }
18239         }
18240     },
18241
18242     /**
18243      * Clears the current filter. Note: with the "remove" option
18244      * set a filter cannot be cleared.
18245      */
18246     clear : function(){
18247         var t = this.tree;
18248         var af = this.filtered;
18249         for(var id in af){
18250             if(typeof id != "function"){
18251                 var n = af[id];
18252                 if(n){
18253                     n.ui.show();
18254                 }
18255             }
18256         }
18257         this.filtered = {};
18258     }
18259 };
18260 /*
18261  * Based on:
18262  * Ext JS Library 1.1.1
18263  * Copyright(c) 2006-2007, Ext JS, LLC.
18264  *
18265  * Originally Released Under LGPL - original licence link has changed is not relivant.
18266  *
18267  * Fork - LGPL
18268  * <script type="text/javascript">
18269  */
18270  
18271
18272 /**
18273  * @class Roo.tree.TreeSorter
18274  * Provides sorting of nodes in a TreePanel
18275  * 
18276  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18277  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18278  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18279  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18280  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18281  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18282  * @constructor
18283  * @param {TreePanel} tree
18284  * @param {Object} config
18285  */
18286 Roo.tree.TreeSorter = function(tree, config){
18287     Roo.apply(this, config);
18288     tree.on("beforechildrenrendered", this.doSort, this);
18289     tree.on("append", this.updateSort, this);
18290     tree.on("insert", this.updateSort, this);
18291     
18292     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18293     var p = this.property || "text";
18294     var sortType = this.sortType;
18295     var fs = this.folderSort;
18296     var cs = this.caseSensitive === true;
18297     var leafAttr = this.leafAttr || 'leaf';
18298
18299     this.sortFn = function(n1, n2){
18300         if(fs){
18301             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18302                 return 1;
18303             }
18304             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18305                 return -1;
18306             }
18307         }
18308         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18309         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18310         if(v1 < v2){
18311                         return dsc ? +1 : -1;
18312                 }else if(v1 > v2){
18313                         return dsc ? -1 : +1;
18314         }else{
18315                 return 0;
18316         }
18317     };
18318 };
18319
18320 Roo.tree.TreeSorter.prototype = {
18321     doSort : function(node){
18322         node.sort(this.sortFn);
18323     },
18324     
18325     compareNodes : function(n1, n2){
18326         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18327     },
18328     
18329     updateSort : function(tree, node){
18330         if(node.childrenRendered){
18331             this.doSort.defer(1, this, [node]);
18332         }
18333     }
18334 };/*
18335  * Based on:
18336  * Ext JS Library 1.1.1
18337  * Copyright(c) 2006-2007, Ext JS, LLC.
18338  *
18339  * Originally Released Under LGPL - original licence link has changed is not relivant.
18340  *
18341  * Fork - LGPL
18342  * <script type="text/javascript">
18343  */
18344
18345 if(Roo.dd.DropZone){
18346     
18347 Roo.tree.TreeDropZone = function(tree, config){
18348     this.allowParentInsert = false;
18349     this.allowContainerDrop = false;
18350     this.appendOnly = false;
18351     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18352     this.tree = tree;
18353     this.lastInsertClass = "x-tree-no-status";
18354     this.dragOverData = {};
18355 };
18356
18357 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18358     ddGroup : "TreeDD",
18359     scroll:  true,
18360     
18361     expandDelay : 1000,
18362     
18363     expandNode : function(node){
18364         if(node.hasChildNodes() && !node.isExpanded()){
18365             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18366         }
18367     },
18368     
18369     queueExpand : function(node){
18370         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18371     },
18372     
18373     cancelExpand : function(){
18374         if(this.expandProcId){
18375             clearTimeout(this.expandProcId);
18376             this.expandProcId = false;
18377         }
18378     },
18379     
18380     isValidDropPoint : function(n, pt, dd, e, data){
18381         if(!n || !data){ return false; }
18382         var targetNode = n.node;
18383         var dropNode = data.node;
18384         // default drop rules
18385         if(!(targetNode && targetNode.isTarget && pt)){
18386             return false;
18387         }
18388         if(pt == "append" && targetNode.allowChildren === false){
18389             return false;
18390         }
18391         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18392             return false;
18393         }
18394         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18395             return false;
18396         }
18397         // reuse the object
18398         var overEvent = this.dragOverData;
18399         overEvent.tree = this.tree;
18400         overEvent.target = targetNode;
18401         overEvent.data = data;
18402         overEvent.point = pt;
18403         overEvent.source = dd;
18404         overEvent.rawEvent = e;
18405         overEvent.dropNode = dropNode;
18406         overEvent.cancel = false;  
18407         var result = this.tree.fireEvent("nodedragover", overEvent);
18408         return overEvent.cancel === false && result !== false;
18409     },
18410     
18411     getDropPoint : function(e, n, dd)
18412     {
18413         var tn = n.node;
18414         if(tn.isRoot){
18415             return tn.allowChildren !== false ? "append" : false; // always append for root
18416         }
18417         var dragEl = n.ddel;
18418         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18419         var y = Roo.lib.Event.getPageY(e);
18420         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18421         
18422         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18423         var noAppend = tn.allowChildren === false;
18424         if(this.appendOnly || tn.parentNode.allowChildren === false){
18425             return noAppend ? false : "append";
18426         }
18427         var noBelow = false;
18428         if(!this.allowParentInsert){
18429             noBelow = tn.hasChildNodes() && tn.isExpanded();
18430         }
18431         var q = (b - t) / (noAppend ? 2 : 3);
18432         if(y >= t && y < (t + q)){
18433             return "above";
18434         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18435             return "below";
18436         }else{
18437             return "append";
18438         }
18439     },
18440     
18441     onNodeEnter : function(n, dd, e, data)
18442     {
18443         this.cancelExpand();
18444     },
18445     
18446     onNodeOver : function(n, dd, e, data)
18447     {
18448        
18449         var pt = this.getDropPoint(e, n, dd);
18450         var node = n.node;
18451         
18452         // auto node expand check
18453         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18454             this.queueExpand(node);
18455         }else if(pt != "append"){
18456             this.cancelExpand();
18457         }
18458         
18459         // set the insert point style on the target node
18460         var returnCls = this.dropNotAllowed;
18461         if(this.isValidDropPoint(n, pt, dd, e, data)){
18462            if(pt){
18463                var el = n.ddel;
18464                var cls;
18465                if(pt == "above"){
18466                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18467                    cls = "x-tree-drag-insert-above";
18468                }else if(pt == "below"){
18469                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18470                    cls = "x-tree-drag-insert-below";
18471                }else{
18472                    returnCls = "x-tree-drop-ok-append";
18473                    cls = "x-tree-drag-append";
18474                }
18475                if(this.lastInsertClass != cls){
18476                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18477                    this.lastInsertClass = cls;
18478                }
18479            }
18480        }
18481        return returnCls;
18482     },
18483     
18484     onNodeOut : function(n, dd, e, data){
18485         
18486         this.cancelExpand();
18487         this.removeDropIndicators(n);
18488     },
18489     
18490     onNodeDrop : function(n, dd, e, data){
18491         var point = this.getDropPoint(e, n, dd);
18492         var targetNode = n.node;
18493         targetNode.ui.startDrop();
18494         if(!this.isValidDropPoint(n, point, dd, e, data)){
18495             targetNode.ui.endDrop();
18496             return false;
18497         }
18498         // first try to find the drop node
18499         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18500         var dropEvent = {
18501             tree : this.tree,
18502             target: targetNode,
18503             data: data,
18504             point: point,
18505             source: dd,
18506             rawEvent: e,
18507             dropNode: dropNode,
18508             cancel: !dropNode   
18509         };
18510         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18511         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18512             targetNode.ui.endDrop();
18513             return false;
18514         }
18515         // allow target changing
18516         targetNode = dropEvent.target;
18517         if(point == "append" && !targetNode.isExpanded()){
18518             targetNode.expand(false, null, function(){
18519                 this.completeDrop(dropEvent);
18520             }.createDelegate(this));
18521         }else{
18522             this.completeDrop(dropEvent);
18523         }
18524         return true;
18525     },
18526     
18527     completeDrop : function(de){
18528         var ns = de.dropNode, p = de.point, t = de.target;
18529         if(!(ns instanceof Array)){
18530             ns = [ns];
18531         }
18532         var n;
18533         for(var i = 0, len = ns.length; i < len; i++){
18534             n = ns[i];
18535             if(p == "above"){
18536                 t.parentNode.insertBefore(n, t);
18537             }else if(p == "below"){
18538                 t.parentNode.insertBefore(n, t.nextSibling);
18539             }else{
18540                 t.appendChild(n);
18541             }
18542         }
18543         n.ui.focus();
18544         if(this.tree.hlDrop){
18545             n.ui.highlight();
18546         }
18547         t.ui.endDrop();
18548         this.tree.fireEvent("nodedrop", de);
18549     },
18550     
18551     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18552         if(this.tree.hlDrop){
18553             dropNode.ui.focus();
18554             dropNode.ui.highlight();
18555         }
18556         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18557     },
18558     
18559     getTree : function(){
18560         return this.tree;
18561     },
18562     
18563     removeDropIndicators : function(n){
18564         if(n && n.ddel){
18565             var el = n.ddel;
18566             Roo.fly(el).removeClass([
18567                     "x-tree-drag-insert-above",
18568                     "x-tree-drag-insert-below",
18569                     "x-tree-drag-append"]);
18570             this.lastInsertClass = "_noclass";
18571         }
18572     },
18573     
18574     beforeDragDrop : function(target, e, id){
18575         this.cancelExpand();
18576         return true;
18577     },
18578     
18579     afterRepair : function(data){
18580         if(data && Roo.enableFx){
18581             data.node.ui.highlight();
18582         }
18583         this.hideProxy();
18584     } 
18585     
18586 });
18587
18588 }
18589 /*
18590  * Based on:
18591  * Ext JS Library 1.1.1
18592  * Copyright(c) 2006-2007, Ext JS, LLC.
18593  *
18594  * Originally Released Under LGPL - original licence link has changed is not relivant.
18595  *
18596  * Fork - LGPL
18597  * <script type="text/javascript">
18598  */
18599  
18600
18601 if(Roo.dd.DragZone){
18602 Roo.tree.TreeDragZone = function(tree, config){
18603     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18604     this.tree = tree;
18605 };
18606
18607 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18608     ddGroup : "TreeDD",
18609    
18610     onBeforeDrag : function(data, e){
18611         var n = data.node;
18612         return n && n.draggable && !n.disabled;
18613     },
18614      
18615     
18616     onInitDrag : function(e){
18617         var data = this.dragData;
18618         this.tree.getSelectionModel().select(data.node);
18619         this.proxy.update("");
18620         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18621         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18622     },
18623     
18624     getRepairXY : function(e, data){
18625         return data.node.ui.getDDRepairXY();
18626     },
18627     
18628     onEndDrag : function(data, e){
18629         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18630         
18631         
18632     },
18633     
18634     onValidDrop : function(dd, e, id){
18635         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18636         this.hideProxy();
18637     },
18638     
18639     beforeInvalidDrop : function(e, id){
18640         // this scrolls the original position back into view
18641         var sm = this.tree.getSelectionModel();
18642         sm.clearSelections();
18643         sm.select(this.dragData.node);
18644     }
18645 });
18646 }/*
18647  * Based on:
18648  * Ext JS Library 1.1.1
18649  * Copyright(c) 2006-2007, Ext JS, LLC.
18650  *
18651  * Originally Released Under LGPL - original licence link has changed is not relivant.
18652  *
18653  * Fork - LGPL
18654  * <script type="text/javascript">
18655  */
18656 /**
18657  * @class Roo.tree.TreeEditor
18658  * @extends Roo.Editor
18659  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18660  * as the editor field.
18661  * @constructor
18662  * @param {Object} config (used to be the tree panel.)
18663  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18664  * 
18665  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18666  * @cfg {Roo.form.TextField|Object} field The field configuration
18667  *
18668  * 
18669  */
18670 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18671     var tree = config;
18672     var field;
18673     if (oldconfig) { // old style..
18674         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18675     } else {
18676         // new style..
18677         tree = config.tree;
18678         config.field = config.field  || {};
18679         config.field.xtype = 'TextField';
18680         field = Roo.factory(config.field, Roo.form);
18681     }
18682     config = config || {};
18683     
18684     
18685     this.addEvents({
18686         /**
18687          * @event beforenodeedit
18688          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18689          * false from the handler of this event.
18690          * @param {Editor} this
18691          * @param {Roo.tree.Node} node 
18692          */
18693         "beforenodeedit" : true
18694     });
18695     
18696     //Roo.log(config);
18697     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18698
18699     this.tree = tree;
18700
18701     tree.on('beforeclick', this.beforeNodeClick, this);
18702     tree.getTreeEl().on('mousedown', this.hide, this);
18703     this.on('complete', this.updateNode, this);
18704     this.on('beforestartedit', this.fitToTree, this);
18705     this.on('startedit', this.bindScroll, this, {delay:10});
18706     this.on('specialkey', this.onSpecialKey, this);
18707 };
18708
18709 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18710     /**
18711      * @cfg {String} alignment
18712      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18713      */
18714     alignment: "l-l",
18715     // inherit
18716     autoSize: false,
18717     /**
18718      * @cfg {Boolean} hideEl
18719      * True to hide the bound element while the editor is displayed (defaults to false)
18720      */
18721     hideEl : false,
18722     /**
18723      * @cfg {String} cls
18724      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18725      */
18726     cls: "x-small-editor x-tree-editor",
18727     /**
18728      * @cfg {Boolean} shim
18729      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18730      */
18731     shim:false,
18732     // inherit
18733     shadow:"frame",
18734     /**
18735      * @cfg {Number} maxWidth
18736      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18737      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18738      * scroll and client offsets into account prior to each edit.
18739      */
18740     maxWidth: 250,
18741
18742     editDelay : 350,
18743
18744     // private
18745     fitToTree : function(ed, el){
18746         var td = this.tree.getTreeEl().dom, nd = el.dom;
18747         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18748             td.scrollLeft = nd.offsetLeft;
18749         }
18750         var w = Math.min(
18751                 this.maxWidth,
18752                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18753         this.setSize(w, '');
18754         
18755         return this.fireEvent('beforenodeedit', this, this.editNode);
18756         
18757     },
18758
18759     // private
18760     triggerEdit : function(node){
18761         this.completeEdit();
18762         this.editNode = node;
18763         this.startEdit(node.ui.textNode, node.text);
18764     },
18765
18766     // private
18767     bindScroll : function(){
18768         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18769     },
18770
18771     // private
18772     beforeNodeClick : function(node, e){
18773         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18774         this.lastClick = new Date();
18775         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18776             e.stopEvent();
18777             this.triggerEdit(node);
18778             return false;
18779         }
18780         return true;
18781     },
18782
18783     // private
18784     updateNode : function(ed, value){
18785         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18786         this.editNode.setText(value);
18787     },
18788
18789     // private
18790     onHide : function(){
18791         Roo.tree.TreeEditor.superclass.onHide.call(this);
18792         if(this.editNode){
18793             this.editNode.ui.focus();
18794         }
18795     },
18796
18797     // private
18798     onSpecialKey : function(field, e){
18799         var k = e.getKey();
18800         if(k == e.ESC){
18801             e.stopEvent();
18802             this.cancelEdit();
18803         }else if(k == e.ENTER && !e.hasModifier()){
18804             e.stopEvent();
18805             this.completeEdit();
18806         }
18807     }
18808 });//<Script type="text/javascript">
18809 /*
18810  * Based on:
18811  * Ext JS Library 1.1.1
18812  * Copyright(c) 2006-2007, Ext JS, LLC.
18813  *
18814  * Originally Released Under LGPL - original licence link has changed is not relivant.
18815  *
18816  * Fork - LGPL
18817  * <script type="text/javascript">
18818  */
18819  
18820 /**
18821  * Not documented??? - probably should be...
18822  */
18823
18824 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18825     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18826     
18827     renderElements : function(n, a, targetNode, bulkRender){
18828         //consel.log("renderElements?");
18829         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18830
18831         var t = n.getOwnerTree();
18832         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18833         
18834         var cols = t.columns;
18835         var bw = t.borderWidth;
18836         var c = cols[0];
18837         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18838          var cb = typeof a.checked == "boolean";
18839         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18840         var colcls = 'x-t-' + tid + '-c0';
18841         var buf = [
18842             '<li class="x-tree-node">',
18843             
18844                 
18845                 '<div class="x-tree-node-el ', a.cls,'">',
18846                     // extran...
18847                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18848                 
18849                 
18850                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18851                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18852                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18853                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18854                            (a.iconCls ? ' '+a.iconCls : ''),
18855                            '" unselectable="on" />',
18856                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18857                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18858                              
18859                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18860                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18861                             '<span unselectable="on" qtip="' + tx + '">',
18862                              tx,
18863                              '</span></a>' ,
18864                     '</div>',
18865                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18866                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18867                  ];
18868         for(var i = 1, len = cols.length; i < len; i++){
18869             c = cols[i];
18870             colcls = 'x-t-' + tid + '-c' +i;
18871             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18872             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18873                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18874                       "</div>");
18875          }
18876          
18877          buf.push(
18878             '</a>',
18879             '<div class="x-clear"></div></div>',
18880             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18881             "</li>");
18882         
18883         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18884             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18885                                 n.nextSibling.ui.getEl(), buf.join(""));
18886         }else{
18887             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18888         }
18889         var el = this.wrap.firstChild;
18890         this.elRow = el;
18891         this.elNode = el.firstChild;
18892         this.ranchor = el.childNodes[1];
18893         this.ctNode = this.wrap.childNodes[1];
18894         var cs = el.firstChild.childNodes;
18895         this.indentNode = cs[0];
18896         this.ecNode = cs[1];
18897         this.iconNode = cs[2];
18898         var index = 3;
18899         if(cb){
18900             this.checkbox = cs[3];
18901             index++;
18902         }
18903         this.anchor = cs[index];
18904         
18905         this.textNode = cs[index].firstChild;
18906         
18907         //el.on("click", this.onClick, this);
18908         //el.on("dblclick", this.onDblClick, this);
18909         
18910         
18911        // console.log(this);
18912     },
18913     initEvents : function(){
18914         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18915         
18916             
18917         var a = this.ranchor;
18918
18919         var el = Roo.get(a);
18920
18921         if(Roo.isOpera){ // opera render bug ignores the CSS
18922             el.setStyle("text-decoration", "none");
18923         }
18924
18925         el.on("click", this.onClick, this);
18926         el.on("dblclick", this.onDblClick, this);
18927         el.on("contextmenu", this.onContextMenu, this);
18928         
18929     },
18930     
18931     /*onSelectedChange : function(state){
18932         if(state){
18933             this.focus();
18934             this.addClass("x-tree-selected");
18935         }else{
18936             //this.blur();
18937             this.removeClass("x-tree-selected");
18938         }
18939     },*/
18940     addClass : function(cls){
18941         if(this.elRow){
18942             Roo.fly(this.elRow).addClass(cls);
18943         }
18944         
18945     },
18946     
18947     
18948     removeClass : function(cls){
18949         if(this.elRow){
18950             Roo.fly(this.elRow).removeClass(cls);
18951         }
18952     }
18953
18954     
18955     
18956 });//<Script type="text/javascript">
18957
18958 /*
18959  * Based on:
18960  * Ext JS Library 1.1.1
18961  * Copyright(c) 2006-2007, Ext JS, LLC.
18962  *
18963  * Originally Released Under LGPL - original licence link has changed is not relivant.
18964  *
18965  * Fork - LGPL
18966  * <script type="text/javascript">
18967  */
18968  
18969
18970 /**
18971  * @class Roo.tree.ColumnTree
18972  * @extends Roo.data.TreePanel
18973  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18974  * @cfg {int} borderWidth  compined right/left border allowance
18975  * @constructor
18976  * @param {String/HTMLElement/Element} el The container element
18977  * @param {Object} config
18978  */
18979 Roo.tree.ColumnTree =  function(el, config)
18980 {
18981    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18982    this.addEvents({
18983         /**
18984         * @event resize
18985         * Fire this event on a container when it resizes
18986         * @param {int} w Width
18987         * @param {int} h Height
18988         */
18989        "resize" : true
18990     });
18991     this.on('resize', this.onResize, this);
18992 };
18993
18994 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
18995     //lines:false,
18996     
18997     
18998     borderWidth: Roo.isBorderBox ? 0 : 2, 
18999     headEls : false,
19000     
19001     render : function(){
19002         // add the header.....
19003        
19004         Roo.tree.ColumnTree.superclass.render.apply(this);
19005         
19006         this.el.addClass('x-column-tree');
19007         
19008         this.headers = this.el.createChild(
19009             {cls:'x-tree-headers'},this.innerCt.dom);
19010    
19011         var cols = this.columns, c;
19012         var totalWidth = 0;
19013         this.headEls = [];
19014         var  len = cols.length;
19015         for(var i = 0; i < len; i++){
19016              c = cols[i];
19017              totalWidth += c.width;
19018             this.headEls.push(this.headers.createChild({
19019                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19020                  cn: {
19021                      cls:'x-tree-hd-text',
19022                      html: c.header
19023                  },
19024                  style:'width:'+(c.width-this.borderWidth)+'px;'
19025              }));
19026         }
19027         this.headers.createChild({cls:'x-clear'});
19028         // prevent floats from wrapping when clipped
19029         this.headers.setWidth(totalWidth);
19030         //this.innerCt.setWidth(totalWidth);
19031         this.innerCt.setStyle({ overflow: 'auto' });
19032         this.onResize(this.width, this.height);
19033              
19034         
19035     },
19036     onResize : function(w,h)
19037     {
19038         this.height = h;
19039         this.width = w;
19040         // resize cols..
19041         this.innerCt.setWidth(this.width);
19042         this.innerCt.setHeight(this.height-20);
19043         
19044         // headers...
19045         var cols = this.columns, c;
19046         var totalWidth = 0;
19047         var expEl = false;
19048         var len = cols.length;
19049         for(var i = 0; i < len; i++){
19050             c = cols[i];
19051             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19052                 // it's the expander..
19053                 expEl  = this.headEls[i];
19054                 continue;
19055             }
19056             totalWidth += c.width;
19057             
19058         }
19059         if (expEl) {
19060             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19061         }
19062         this.headers.setWidth(w-20);
19063
19064         
19065         
19066         
19067     }
19068 });
19069 /*
19070  * Based on:
19071  * Ext JS Library 1.1.1
19072  * Copyright(c) 2006-2007, Ext JS, LLC.
19073  *
19074  * Originally Released Under LGPL - original licence link has changed is not relivant.
19075  *
19076  * Fork - LGPL
19077  * <script type="text/javascript">
19078  */
19079  
19080 /**
19081  * @class Roo.menu.Menu
19082  * @extends Roo.util.Observable
19083  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19084  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19085  * @constructor
19086  * Creates a new Menu
19087  * @param {Object} config Configuration options
19088  */
19089 Roo.menu.Menu = function(config){
19090     Roo.apply(this, config);
19091     this.id = this.id || Roo.id();
19092     this.addEvents({
19093         /**
19094          * @event beforeshow
19095          * Fires before this menu is displayed
19096          * @param {Roo.menu.Menu} this
19097          */
19098         beforeshow : true,
19099         /**
19100          * @event beforehide
19101          * Fires before this menu is hidden
19102          * @param {Roo.menu.Menu} this
19103          */
19104         beforehide : true,
19105         /**
19106          * @event show
19107          * Fires after this menu is displayed
19108          * @param {Roo.menu.Menu} this
19109          */
19110         show : true,
19111         /**
19112          * @event hide
19113          * Fires after this menu is hidden
19114          * @param {Roo.menu.Menu} this
19115          */
19116         hide : true,
19117         /**
19118          * @event click
19119          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19120          * @param {Roo.menu.Menu} this
19121          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19122          * @param {Roo.EventObject} e
19123          */
19124         click : true,
19125         /**
19126          * @event mouseover
19127          * Fires when the mouse is hovering over this menu
19128          * @param {Roo.menu.Menu} this
19129          * @param {Roo.EventObject} e
19130          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19131          */
19132         mouseover : true,
19133         /**
19134          * @event mouseout
19135          * Fires when the mouse exits this menu
19136          * @param {Roo.menu.Menu} this
19137          * @param {Roo.EventObject} e
19138          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19139          */
19140         mouseout : true,
19141         /**
19142          * @event itemclick
19143          * Fires when a menu item contained in this menu is clicked
19144          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19145          * @param {Roo.EventObject} e
19146          */
19147         itemclick: true
19148     });
19149     if (this.registerMenu) {
19150         Roo.menu.MenuMgr.register(this);
19151     }
19152     
19153     var mis = this.items;
19154     this.items = new Roo.util.MixedCollection();
19155     if(mis){
19156         this.add.apply(this, mis);
19157     }
19158 };
19159
19160 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19161     /**
19162      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19163      */
19164     minWidth : 120,
19165     /**
19166      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19167      * for bottom-right shadow (defaults to "sides")
19168      */
19169     shadow : "sides",
19170     /**
19171      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19172      * this menu (defaults to "tl-tr?")
19173      */
19174     subMenuAlign : "tl-tr?",
19175     /**
19176      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19177      * relative to its element of origin (defaults to "tl-bl?")
19178      */
19179     defaultAlign : "tl-bl?",
19180     /**
19181      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19182      */
19183     allowOtherMenus : false,
19184     /**
19185      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19186      */
19187     registerMenu : true,
19188
19189     hidden:true,
19190
19191     // private
19192     render : function(){
19193         if(this.el){
19194             return;
19195         }
19196         var el = this.el = new Roo.Layer({
19197             cls: "x-menu",
19198             shadow:this.shadow,
19199             constrain: false,
19200             parentEl: this.parentEl || document.body,
19201             zindex:15000
19202         });
19203
19204         this.keyNav = new Roo.menu.MenuNav(this);
19205
19206         if(this.plain){
19207             el.addClass("x-menu-plain");
19208         }
19209         if(this.cls){
19210             el.addClass(this.cls);
19211         }
19212         // generic focus element
19213         this.focusEl = el.createChild({
19214             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19215         });
19216         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19217         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19218         
19219         ul.on("mouseover", this.onMouseOver, this);
19220         ul.on("mouseout", this.onMouseOut, this);
19221         this.items.each(function(item){
19222             if (item.hidden) {
19223                 return;
19224             }
19225             
19226             var li = document.createElement("li");
19227             li.className = "x-menu-list-item";
19228             ul.dom.appendChild(li);
19229             item.render(li, this);
19230         }, this);
19231         this.ul = ul;
19232         this.autoWidth();
19233     },
19234
19235     // private
19236     autoWidth : function(){
19237         var el = this.el, ul = this.ul;
19238         if(!el){
19239             return;
19240         }
19241         var w = this.width;
19242         if(w){
19243             el.setWidth(w);
19244         }else if(Roo.isIE){
19245             el.setWidth(this.minWidth);
19246             var t = el.dom.offsetWidth; // force recalc
19247             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19248         }
19249     },
19250
19251     // private
19252     delayAutoWidth : function(){
19253         if(this.rendered){
19254             if(!this.awTask){
19255                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19256             }
19257             this.awTask.delay(20);
19258         }
19259     },
19260
19261     // private
19262     findTargetItem : function(e){
19263         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19264         if(t && t.menuItemId){
19265             return this.items.get(t.menuItemId);
19266         }
19267     },
19268
19269     // private
19270     onClick : function(e){
19271         Roo.log("menu.onClick");
19272         var t = this.findTargetItem(e);
19273         if(!t){
19274             return;
19275         }
19276         Roo.log(e);
19277         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19278             if(t == this.activeItem && t.shouldDeactivate(e)){
19279                 this.activeItem.deactivate();
19280                 delete this.activeItem;
19281                 return;
19282             }
19283             if(t.canActivate){
19284                 this.setActiveItem(t, true);
19285             }
19286             return;
19287             
19288             
19289         }
19290         
19291         t.onClick(e);
19292         this.fireEvent("click", this, t, e);
19293     },
19294
19295     // private
19296     setActiveItem : function(item, autoExpand){
19297         if(item != this.activeItem){
19298             if(this.activeItem){
19299                 this.activeItem.deactivate();
19300             }
19301             this.activeItem = item;
19302             item.activate(autoExpand);
19303         }else if(autoExpand){
19304             item.expandMenu();
19305         }
19306     },
19307
19308     // private
19309     tryActivate : function(start, step){
19310         var items = this.items;
19311         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19312             var item = items.get(i);
19313             if(!item.disabled && item.canActivate){
19314                 this.setActiveItem(item, false);
19315                 return item;
19316             }
19317         }
19318         return false;
19319     },
19320
19321     // private
19322     onMouseOver : function(e){
19323         var t;
19324         if(t = this.findTargetItem(e)){
19325             if(t.canActivate && !t.disabled){
19326                 this.setActiveItem(t, true);
19327             }
19328         }
19329         this.fireEvent("mouseover", this, e, t);
19330     },
19331
19332     // private
19333     onMouseOut : function(e){
19334         var t;
19335         if(t = this.findTargetItem(e)){
19336             if(t == this.activeItem && t.shouldDeactivate(e)){
19337                 this.activeItem.deactivate();
19338                 delete this.activeItem;
19339             }
19340         }
19341         this.fireEvent("mouseout", this, e, t);
19342     },
19343
19344     /**
19345      * Read-only.  Returns true if the menu is currently displayed, else false.
19346      * @type Boolean
19347      */
19348     isVisible : function(){
19349         return this.el && !this.hidden;
19350     },
19351
19352     /**
19353      * Displays this menu relative to another element
19354      * @param {String/HTMLElement/Roo.Element} element The element to align to
19355      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19356      * the element (defaults to this.defaultAlign)
19357      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19358      */
19359     show : function(el, pos, parentMenu){
19360         this.parentMenu = parentMenu;
19361         if(!this.el){
19362             this.render();
19363         }
19364         this.fireEvent("beforeshow", this);
19365         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19366     },
19367
19368     /**
19369      * Displays this menu at a specific xy position
19370      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19371      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19372      */
19373     showAt : function(xy, parentMenu, /* private: */_e){
19374         this.parentMenu = parentMenu;
19375         if(!this.el){
19376             this.render();
19377         }
19378         if(_e !== false){
19379             this.fireEvent("beforeshow", this);
19380             xy = this.el.adjustForConstraints(xy);
19381         }
19382         this.el.setXY(xy);
19383         this.el.show();
19384         this.hidden = false;
19385         this.focus();
19386         this.fireEvent("show", this);
19387     },
19388
19389     focus : function(){
19390         if(!this.hidden){
19391             this.doFocus.defer(50, this);
19392         }
19393     },
19394
19395     doFocus : function(){
19396         if(!this.hidden){
19397             this.focusEl.focus();
19398         }
19399     },
19400
19401     /**
19402      * Hides this menu and optionally all parent menus
19403      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19404      */
19405     hide : function(deep){
19406         if(this.el && this.isVisible()){
19407             this.fireEvent("beforehide", this);
19408             if(this.activeItem){
19409                 this.activeItem.deactivate();
19410                 this.activeItem = null;
19411             }
19412             this.el.hide();
19413             this.hidden = true;
19414             this.fireEvent("hide", this);
19415         }
19416         if(deep === true && this.parentMenu){
19417             this.parentMenu.hide(true);
19418         }
19419     },
19420
19421     /**
19422      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19423      * Any of the following are valid:
19424      * <ul>
19425      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19426      * <li>An HTMLElement object which will be converted to a menu item</li>
19427      * <li>A menu item config object that will be created as a new menu item</li>
19428      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19429      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19430      * </ul>
19431      * Usage:
19432      * <pre><code>
19433 // Create the menu
19434 var menu = new Roo.menu.Menu();
19435
19436 // Create a menu item to add by reference
19437 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19438
19439 // Add a bunch of items at once using different methods.
19440 // Only the last item added will be returned.
19441 var item = menu.add(
19442     menuItem,                // add existing item by ref
19443     'Dynamic Item',          // new TextItem
19444     '-',                     // new separator
19445     { text: 'Config Item' }  // new item by config
19446 );
19447 </code></pre>
19448      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19449      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19450      */
19451     add : function(){
19452         var a = arguments, l = a.length, item;
19453         for(var i = 0; i < l; i++){
19454             var el = a[i];
19455             if ((typeof(el) == "object") && el.xtype && el.xns) {
19456                 el = Roo.factory(el, Roo.menu);
19457             }
19458             
19459             if(el.render){ // some kind of Item
19460                 item = this.addItem(el);
19461             }else if(typeof el == "string"){ // string
19462                 if(el == "separator" || el == "-"){
19463                     item = this.addSeparator();
19464                 }else{
19465                     item = this.addText(el);
19466                 }
19467             }else if(el.tagName || el.el){ // element
19468                 item = this.addElement(el);
19469             }else if(typeof el == "object"){ // must be menu item config?
19470                 item = this.addMenuItem(el);
19471             }
19472         }
19473         return item;
19474     },
19475
19476     /**
19477      * Returns this menu's underlying {@link Roo.Element} object
19478      * @return {Roo.Element} The element
19479      */
19480     getEl : function(){
19481         if(!this.el){
19482             this.render();
19483         }
19484         return this.el;
19485     },
19486
19487     /**
19488      * Adds a separator bar to the menu
19489      * @return {Roo.menu.Item} The menu item that was added
19490      */
19491     addSeparator : function(){
19492         return this.addItem(new Roo.menu.Separator());
19493     },
19494
19495     /**
19496      * Adds an {@link Roo.Element} object to the menu
19497      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19498      * @return {Roo.menu.Item} The menu item that was added
19499      */
19500     addElement : function(el){
19501         return this.addItem(new Roo.menu.BaseItem(el));
19502     },
19503
19504     /**
19505      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19506      * @param {Roo.menu.Item} item The menu item to add
19507      * @return {Roo.menu.Item} The menu item that was added
19508      */
19509     addItem : function(item){
19510         this.items.add(item);
19511         if(this.ul){
19512             var li = document.createElement("li");
19513             li.className = "x-menu-list-item";
19514             this.ul.dom.appendChild(li);
19515             item.render(li, this);
19516             this.delayAutoWidth();
19517         }
19518         return item;
19519     },
19520
19521     /**
19522      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19523      * @param {Object} config A MenuItem config object
19524      * @return {Roo.menu.Item} The menu item that was added
19525      */
19526     addMenuItem : function(config){
19527         if(!(config instanceof Roo.menu.Item)){
19528             if(typeof config.checked == "boolean"){ // must be check menu item config?
19529                 config = new Roo.menu.CheckItem(config);
19530             }else{
19531                 config = new Roo.menu.Item(config);
19532             }
19533         }
19534         return this.addItem(config);
19535     },
19536
19537     /**
19538      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19539      * @param {String} text The text to display in the menu item
19540      * @return {Roo.menu.Item} The menu item that was added
19541      */
19542     addText : function(text){
19543         return this.addItem(new Roo.menu.TextItem({ text : text }));
19544     },
19545
19546     /**
19547      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19548      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19549      * @param {Roo.menu.Item} item The menu item to add
19550      * @return {Roo.menu.Item} The menu item that was added
19551      */
19552     insert : function(index, item){
19553         this.items.insert(index, item);
19554         if(this.ul){
19555             var li = document.createElement("li");
19556             li.className = "x-menu-list-item";
19557             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19558             item.render(li, this);
19559             this.delayAutoWidth();
19560         }
19561         return item;
19562     },
19563
19564     /**
19565      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19566      * @param {Roo.menu.Item} item The menu item to remove
19567      */
19568     remove : function(item){
19569         this.items.removeKey(item.id);
19570         item.destroy();
19571     },
19572
19573     /**
19574      * Removes and destroys all items in the menu
19575      */
19576     removeAll : function(){
19577         var f;
19578         while(f = this.items.first()){
19579             this.remove(f);
19580         }
19581     }
19582 });
19583
19584 // MenuNav is a private utility class used internally by the Menu
19585 Roo.menu.MenuNav = function(menu){
19586     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19587     this.scope = this.menu = menu;
19588 };
19589
19590 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19591     doRelay : function(e, h){
19592         var k = e.getKey();
19593         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19594             this.menu.tryActivate(0, 1);
19595             return false;
19596         }
19597         return h.call(this.scope || this, e, this.menu);
19598     },
19599
19600     up : function(e, m){
19601         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19602             m.tryActivate(m.items.length-1, -1);
19603         }
19604     },
19605
19606     down : function(e, m){
19607         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19608             m.tryActivate(0, 1);
19609         }
19610     },
19611
19612     right : function(e, m){
19613         if(m.activeItem){
19614             m.activeItem.expandMenu(true);
19615         }
19616     },
19617
19618     left : function(e, m){
19619         m.hide();
19620         if(m.parentMenu && m.parentMenu.activeItem){
19621             m.parentMenu.activeItem.activate();
19622         }
19623     },
19624
19625     enter : function(e, m){
19626         if(m.activeItem){
19627             e.stopPropagation();
19628             m.activeItem.onClick(e);
19629             m.fireEvent("click", this, m.activeItem);
19630             return true;
19631         }
19632     }
19633 });/*
19634  * Based on:
19635  * Ext JS Library 1.1.1
19636  * Copyright(c) 2006-2007, Ext JS, LLC.
19637  *
19638  * Originally Released Under LGPL - original licence link has changed is not relivant.
19639  *
19640  * Fork - LGPL
19641  * <script type="text/javascript">
19642  */
19643  
19644 /**
19645  * @class Roo.menu.MenuMgr
19646  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19647  * @singleton
19648  */
19649 Roo.menu.MenuMgr = function(){
19650    var menus, active, groups = {}, attached = false, lastShow = new Date();
19651
19652    // private - called when first menu is created
19653    function init(){
19654        menus = {};
19655        active = new Roo.util.MixedCollection();
19656        Roo.get(document).addKeyListener(27, function(){
19657            if(active.length > 0){
19658                hideAll();
19659            }
19660        });
19661    }
19662
19663    // private
19664    function hideAll(){
19665        if(active && active.length > 0){
19666            var c = active.clone();
19667            c.each(function(m){
19668                m.hide();
19669            });
19670        }
19671    }
19672
19673    // private
19674    function onHide(m){
19675        active.remove(m);
19676        if(active.length < 1){
19677            Roo.get(document).un("mousedown", onMouseDown);
19678            attached = false;
19679        }
19680    }
19681
19682    // private
19683    function onShow(m){
19684        var last = active.last();
19685        lastShow = new Date();
19686        active.add(m);
19687        if(!attached){
19688            Roo.get(document).on("mousedown", onMouseDown);
19689            attached = true;
19690        }
19691        if(m.parentMenu){
19692           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19693           m.parentMenu.activeChild = m;
19694        }else if(last && last.isVisible()){
19695           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19696        }
19697    }
19698
19699    // private
19700    function onBeforeHide(m){
19701        if(m.activeChild){
19702            m.activeChild.hide();
19703        }
19704        if(m.autoHideTimer){
19705            clearTimeout(m.autoHideTimer);
19706            delete m.autoHideTimer;
19707        }
19708    }
19709
19710    // private
19711    function onBeforeShow(m){
19712        var pm = m.parentMenu;
19713        if(!pm && !m.allowOtherMenus){
19714            hideAll();
19715        }else if(pm && pm.activeChild && active != m){
19716            pm.activeChild.hide();
19717        }
19718    }
19719
19720    // private
19721    function onMouseDown(e){
19722        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19723            hideAll();
19724        }
19725    }
19726
19727    // private
19728    function onBeforeCheck(mi, state){
19729        if(state){
19730            var g = groups[mi.group];
19731            for(var i = 0, l = g.length; i < l; i++){
19732                if(g[i] != mi){
19733                    g[i].setChecked(false);
19734                }
19735            }
19736        }
19737    }
19738
19739    return {
19740
19741        /**
19742         * Hides all menus that are currently visible
19743         */
19744        hideAll : function(){
19745             hideAll();  
19746        },
19747
19748        // private
19749        register : function(menu){
19750            if(!menus){
19751                init();
19752            }
19753            menus[menu.id] = menu;
19754            menu.on("beforehide", onBeforeHide);
19755            menu.on("hide", onHide);
19756            menu.on("beforeshow", onBeforeShow);
19757            menu.on("show", onShow);
19758            var g = menu.group;
19759            if(g && menu.events["checkchange"]){
19760                if(!groups[g]){
19761                    groups[g] = [];
19762                }
19763                groups[g].push(menu);
19764                menu.on("checkchange", onCheck);
19765            }
19766        },
19767
19768         /**
19769          * Returns a {@link Roo.menu.Menu} object
19770          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19771          * be used to generate and return a new Menu instance.
19772          */
19773        get : function(menu){
19774            if(typeof menu == "string"){ // menu id
19775                return menus[menu];
19776            }else if(menu.events){  // menu instance
19777                return menu;
19778            }else if(typeof menu.length == 'number'){ // array of menu items?
19779                return new Roo.menu.Menu({items:menu});
19780            }else{ // otherwise, must be a config
19781                return new Roo.menu.Menu(menu);
19782            }
19783        },
19784
19785        // private
19786        unregister : function(menu){
19787            delete menus[menu.id];
19788            menu.un("beforehide", onBeforeHide);
19789            menu.un("hide", onHide);
19790            menu.un("beforeshow", onBeforeShow);
19791            menu.un("show", onShow);
19792            var g = menu.group;
19793            if(g && menu.events["checkchange"]){
19794                groups[g].remove(menu);
19795                menu.un("checkchange", onCheck);
19796            }
19797        },
19798
19799        // private
19800        registerCheckable : function(menuItem){
19801            var g = menuItem.group;
19802            if(g){
19803                if(!groups[g]){
19804                    groups[g] = [];
19805                }
19806                groups[g].push(menuItem);
19807                menuItem.on("beforecheckchange", onBeforeCheck);
19808            }
19809        },
19810
19811        // private
19812        unregisterCheckable : function(menuItem){
19813            var g = menuItem.group;
19814            if(g){
19815                groups[g].remove(menuItem);
19816                menuItem.un("beforecheckchange", onBeforeCheck);
19817            }
19818        }
19819    };
19820 }();/*
19821  * Based on:
19822  * Ext JS Library 1.1.1
19823  * Copyright(c) 2006-2007, Ext JS, LLC.
19824  *
19825  * Originally Released Under LGPL - original licence link has changed is not relivant.
19826  *
19827  * Fork - LGPL
19828  * <script type="text/javascript">
19829  */
19830  
19831
19832 /**
19833  * @class Roo.menu.BaseItem
19834  * @extends Roo.Component
19835  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19836  * management and base configuration options shared by all menu components.
19837  * @constructor
19838  * Creates a new BaseItem
19839  * @param {Object} config Configuration options
19840  */
19841 Roo.menu.BaseItem = function(config){
19842     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19843
19844     this.addEvents({
19845         /**
19846          * @event click
19847          * Fires when this item is clicked
19848          * @param {Roo.menu.BaseItem} this
19849          * @param {Roo.EventObject} e
19850          */
19851         click: true,
19852         /**
19853          * @event activate
19854          * Fires when this item is activated
19855          * @param {Roo.menu.BaseItem} this
19856          */
19857         activate : true,
19858         /**
19859          * @event deactivate
19860          * Fires when this item is deactivated
19861          * @param {Roo.menu.BaseItem} this
19862          */
19863         deactivate : true
19864     });
19865
19866     if(this.handler){
19867         this.on("click", this.handler, this.scope, true);
19868     }
19869 };
19870
19871 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19872     /**
19873      * @cfg {Function} handler
19874      * A function that will handle the click event of this menu item (defaults to undefined)
19875      */
19876     /**
19877      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19878      */
19879     canActivate : false,
19880     
19881      /**
19882      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19883      */
19884     hidden: false,
19885     
19886     /**
19887      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19888      */
19889     activeClass : "x-menu-item-active",
19890     /**
19891      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19892      */
19893     hideOnClick : true,
19894     /**
19895      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19896      */
19897     hideDelay : 100,
19898
19899     // private
19900     ctype: "Roo.menu.BaseItem",
19901
19902     // private
19903     actionMode : "container",
19904
19905     // private
19906     render : function(container, parentMenu){
19907         this.parentMenu = parentMenu;
19908         Roo.menu.BaseItem.superclass.render.call(this, container);
19909         this.container.menuItemId = this.id;
19910     },
19911
19912     // private
19913     onRender : function(container, position){
19914         this.el = Roo.get(this.el);
19915         container.dom.appendChild(this.el.dom);
19916     },
19917
19918     // private
19919     onClick : function(e){
19920         if(!this.disabled && this.fireEvent("click", this, e) !== false
19921                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19922             this.handleClick(e);
19923         }else{
19924             e.stopEvent();
19925         }
19926     },
19927
19928     // private
19929     activate : function(){
19930         if(this.disabled){
19931             return false;
19932         }
19933         var li = this.container;
19934         li.addClass(this.activeClass);
19935         this.region = li.getRegion().adjust(2, 2, -2, -2);
19936         this.fireEvent("activate", this);
19937         return true;
19938     },
19939
19940     // private
19941     deactivate : function(){
19942         this.container.removeClass(this.activeClass);
19943         this.fireEvent("deactivate", this);
19944     },
19945
19946     // private
19947     shouldDeactivate : function(e){
19948         return !this.region || !this.region.contains(e.getPoint());
19949     },
19950
19951     // private
19952     handleClick : function(e){
19953         if(this.hideOnClick){
19954             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19955         }
19956     },
19957
19958     // private
19959     expandMenu : function(autoActivate){
19960         // do nothing
19961     },
19962
19963     // private
19964     hideMenu : function(){
19965         // do nothing
19966     }
19967 });/*
19968  * Based on:
19969  * Ext JS Library 1.1.1
19970  * Copyright(c) 2006-2007, Ext JS, LLC.
19971  *
19972  * Originally Released Under LGPL - original licence link has changed is not relivant.
19973  *
19974  * Fork - LGPL
19975  * <script type="text/javascript">
19976  */
19977  
19978 /**
19979  * @class Roo.menu.Adapter
19980  * @extends Roo.menu.BaseItem
19981  * 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.
19982  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19983  * @constructor
19984  * Creates a new Adapter
19985  * @param {Object} config Configuration options
19986  */
19987 Roo.menu.Adapter = function(component, config){
19988     Roo.menu.Adapter.superclass.constructor.call(this, config);
19989     this.component = component;
19990 };
19991 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19992     // private
19993     canActivate : true,
19994
19995     // private
19996     onRender : function(container, position){
19997         this.component.render(container);
19998         this.el = this.component.getEl();
19999     },
20000
20001     // private
20002     activate : function(){
20003         if(this.disabled){
20004             return false;
20005         }
20006         this.component.focus();
20007         this.fireEvent("activate", this);
20008         return true;
20009     },
20010
20011     // private
20012     deactivate : function(){
20013         this.fireEvent("deactivate", this);
20014     },
20015
20016     // private
20017     disable : function(){
20018         this.component.disable();
20019         Roo.menu.Adapter.superclass.disable.call(this);
20020     },
20021
20022     // private
20023     enable : function(){
20024         this.component.enable();
20025         Roo.menu.Adapter.superclass.enable.call(this);
20026     }
20027 });/*
20028  * Based on:
20029  * Ext JS Library 1.1.1
20030  * Copyright(c) 2006-2007, Ext JS, LLC.
20031  *
20032  * Originally Released Under LGPL - original licence link has changed is not relivant.
20033  *
20034  * Fork - LGPL
20035  * <script type="text/javascript">
20036  */
20037
20038 /**
20039  * @class Roo.menu.TextItem
20040  * @extends Roo.menu.BaseItem
20041  * Adds a static text string to a menu, usually used as either a heading or group separator.
20042  * Note: old style constructor with text is still supported.
20043  * 
20044  * @constructor
20045  * Creates a new TextItem
20046  * @param {Object} cfg Configuration
20047  */
20048 Roo.menu.TextItem = function(cfg){
20049     if (typeof(cfg) == 'string') {
20050         this.text = cfg;
20051     } else {
20052         Roo.apply(this,cfg);
20053     }
20054     
20055     Roo.menu.TextItem.superclass.constructor.call(this);
20056 };
20057
20058 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20059     /**
20060      * @cfg {Boolean} text Text to show on item.
20061      */
20062     text : '',
20063     
20064     /**
20065      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20066      */
20067     hideOnClick : false,
20068     /**
20069      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20070      */
20071     itemCls : "x-menu-text",
20072
20073     // private
20074     onRender : function(){
20075         var s = document.createElement("span");
20076         s.className = this.itemCls;
20077         s.innerHTML = this.text;
20078         this.el = s;
20079         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20080     }
20081 });/*
20082  * Based on:
20083  * Ext JS Library 1.1.1
20084  * Copyright(c) 2006-2007, Ext JS, LLC.
20085  *
20086  * Originally Released Under LGPL - original licence link has changed is not relivant.
20087  *
20088  * Fork - LGPL
20089  * <script type="text/javascript">
20090  */
20091
20092 /**
20093  * @class Roo.menu.Separator
20094  * @extends Roo.menu.BaseItem
20095  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20096  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20097  * @constructor
20098  * @param {Object} config Configuration options
20099  */
20100 Roo.menu.Separator = function(config){
20101     Roo.menu.Separator.superclass.constructor.call(this, config);
20102 };
20103
20104 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20105     /**
20106      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20107      */
20108     itemCls : "x-menu-sep",
20109     /**
20110      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20111      */
20112     hideOnClick : false,
20113
20114     // private
20115     onRender : function(li){
20116         var s = document.createElement("span");
20117         s.className = this.itemCls;
20118         s.innerHTML = "&#160;";
20119         this.el = s;
20120         li.addClass("x-menu-sep-li");
20121         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20122     }
20123 });/*
20124  * Based on:
20125  * Ext JS Library 1.1.1
20126  * Copyright(c) 2006-2007, Ext JS, LLC.
20127  *
20128  * Originally Released Under LGPL - original licence link has changed is not relivant.
20129  *
20130  * Fork - LGPL
20131  * <script type="text/javascript">
20132  */
20133 /**
20134  * @class Roo.menu.Item
20135  * @extends Roo.menu.BaseItem
20136  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20137  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20138  * activation and click handling.
20139  * @constructor
20140  * Creates a new Item
20141  * @param {Object} config Configuration options
20142  */
20143 Roo.menu.Item = function(config){
20144     Roo.menu.Item.superclass.constructor.call(this, config);
20145     if(this.menu){
20146         this.menu = Roo.menu.MenuMgr.get(this.menu);
20147     }
20148 };
20149 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20150     
20151     /**
20152      * @cfg {String} text
20153      * The text to show on the menu item.
20154      */
20155     text: '',
20156      /**
20157      * @cfg {String} HTML to render in menu
20158      * The text to show on the menu item (HTML version).
20159      */
20160     html: '',
20161     /**
20162      * @cfg {String} icon
20163      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20164      */
20165     icon: undefined,
20166     /**
20167      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20168      */
20169     itemCls : "x-menu-item",
20170     /**
20171      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20172      */
20173     canActivate : true,
20174     /**
20175      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20176      */
20177     showDelay: 200,
20178     // doc'd in BaseItem
20179     hideDelay: 200,
20180
20181     // private
20182     ctype: "Roo.menu.Item",
20183     
20184     // private
20185     onRender : function(container, position){
20186         var el = document.createElement("a");
20187         el.hideFocus = true;
20188         el.unselectable = "on";
20189         el.href = this.href || "#";
20190         if(this.hrefTarget){
20191             el.target = this.hrefTarget;
20192         }
20193         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20194         
20195         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20196         
20197         el.innerHTML = String.format(
20198                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20199                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20200         this.el = el;
20201         Roo.menu.Item.superclass.onRender.call(this, container, position);
20202     },
20203
20204     /**
20205      * Sets the text to display in this menu item
20206      * @param {String} text The text to display
20207      * @param {Boolean} isHTML true to indicate text is pure html.
20208      */
20209     setText : function(text, isHTML){
20210         if (isHTML) {
20211             this.html = text;
20212         } else {
20213             this.text = text;
20214             this.html = '';
20215         }
20216         if(this.rendered){
20217             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20218      
20219             this.el.update(String.format(
20220                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20221                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20222             this.parentMenu.autoWidth();
20223         }
20224     },
20225
20226     // private
20227     handleClick : function(e){
20228         if(!this.href){ // if no link defined, stop the event automatically
20229             e.stopEvent();
20230         }
20231         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20232     },
20233
20234     // private
20235     activate : function(autoExpand){
20236         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20237             this.focus();
20238             if(autoExpand){
20239                 this.expandMenu();
20240             }
20241         }
20242         return true;
20243     },
20244
20245     // private
20246     shouldDeactivate : function(e){
20247         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20248             if(this.menu && this.menu.isVisible()){
20249                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20250             }
20251             return true;
20252         }
20253         return false;
20254     },
20255
20256     // private
20257     deactivate : function(){
20258         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20259         this.hideMenu();
20260     },
20261
20262     // private
20263     expandMenu : function(autoActivate){
20264         if(!this.disabled && this.menu){
20265             clearTimeout(this.hideTimer);
20266             delete this.hideTimer;
20267             if(!this.menu.isVisible() && !this.showTimer){
20268                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20269             }else if (this.menu.isVisible() && autoActivate){
20270                 this.menu.tryActivate(0, 1);
20271             }
20272         }
20273     },
20274
20275     // private
20276     deferExpand : function(autoActivate){
20277         delete this.showTimer;
20278         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20279         if(autoActivate){
20280             this.menu.tryActivate(0, 1);
20281         }
20282     },
20283
20284     // private
20285     hideMenu : function(){
20286         clearTimeout(this.showTimer);
20287         delete this.showTimer;
20288         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20289             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20290         }
20291     },
20292
20293     // private
20294     deferHide : function(){
20295         delete this.hideTimer;
20296         this.menu.hide();
20297     }
20298 });/*
20299  * Based on:
20300  * Ext JS Library 1.1.1
20301  * Copyright(c) 2006-2007, Ext JS, LLC.
20302  *
20303  * Originally Released Under LGPL - original licence link has changed is not relivant.
20304  *
20305  * Fork - LGPL
20306  * <script type="text/javascript">
20307  */
20308  
20309 /**
20310  * @class Roo.menu.CheckItem
20311  * @extends Roo.menu.Item
20312  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20313  * @constructor
20314  * Creates a new CheckItem
20315  * @param {Object} config Configuration options
20316  */
20317 Roo.menu.CheckItem = function(config){
20318     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20319     this.addEvents({
20320         /**
20321          * @event beforecheckchange
20322          * Fires before the checked value is set, providing an opportunity to cancel if needed
20323          * @param {Roo.menu.CheckItem} this
20324          * @param {Boolean} checked The new checked value that will be set
20325          */
20326         "beforecheckchange" : true,
20327         /**
20328          * @event checkchange
20329          * Fires after the checked value has been set
20330          * @param {Roo.menu.CheckItem} this
20331          * @param {Boolean} checked The checked value that was set
20332          */
20333         "checkchange" : true
20334     });
20335     if(this.checkHandler){
20336         this.on('checkchange', this.checkHandler, this.scope);
20337     }
20338 };
20339 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20340     /**
20341      * @cfg {String} group
20342      * All check items with the same group name will automatically be grouped into a single-select
20343      * radio button group (defaults to '')
20344      */
20345     /**
20346      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20347      */
20348     itemCls : "x-menu-item x-menu-check-item",
20349     /**
20350      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20351      */
20352     groupClass : "x-menu-group-item",
20353
20354     /**
20355      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20356      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20357      * initialized with checked = true will be rendered as checked.
20358      */
20359     checked: false,
20360
20361     // private
20362     ctype: "Roo.menu.CheckItem",
20363
20364     // private
20365     onRender : function(c){
20366         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20367         if(this.group){
20368             this.el.addClass(this.groupClass);
20369         }
20370         Roo.menu.MenuMgr.registerCheckable(this);
20371         if(this.checked){
20372             this.checked = false;
20373             this.setChecked(true, true);
20374         }
20375     },
20376
20377     // private
20378     destroy : function(){
20379         if(this.rendered){
20380             Roo.menu.MenuMgr.unregisterCheckable(this);
20381         }
20382         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20383     },
20384
20385     /**
20386      * Set the checked state of this item
20387      * @param {Boolean} checked The new checked value
20388      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20389      */
20390     setChecked : function(state, suppressEvent){
20391         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20392             if(this.container){
20393                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20394             }
20395             this.checked = state;
20396             if(suppressEvent !== true){
20397                 this.fireEvent("checkchange", this, state);
20398             }
20399         }
20400     },
20401
20402     // private
20403     handleClick : function(e){
20404        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20405            this.setChecked(!this.checked);
20406        }
20407        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20408     }
20409 });/*
20410  * Based on:
20411  * Ext JS Library 1.1.1
20412  * Copyright(c) 2006-2007, Ext JS, LLC.
20413  *
20414  * Originally Released Under LGPL - original licence link has changed is not relivant.
20415  *
20416  * Fork - LGPL
20417  * <script type="text/javascript">
20418  */
20419  
20420 /**
20421  * @class Roo.menu.DateItem
20422  * @extends Roo.menu.Adapter
20423  * A menu item that wraps the {@link Roo.DatPicker} component.
20424  * @constructor
20425  * Creates a new DateItem
20426  * @param {Object} config Configuration options
20427  */
20428 Roo.menu.DateItem = function(config){
20429     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20430     /** The Roo.DatePicker object @type Roo.DatePicker */
20431     this.picker = this.component;
20432     this.addEvents({select: true});
20433     
20434     this.picker.on("render", function(picker){
20435         picker.getEl().swallowEvent("click");
20436         picker.container.addClass("x-menu-date-item");
20437     });
20438
20439     this.picker.on("select", this.onSelect, this);
20440 };
20441
20442 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20443     // private
20444     onSelect : function(picker, date){
20445         this.fireEvent("select", this, date, picker);
20446         Roo.menu.DateItem.superclass.handleClick.call(this);
20447     }
20448 });/*
20449  * Based on:
20450  * Ext JS Library 1.1.1
20451  * Copyright(c) 2006-2007, Ext JS, LLC.
20452  *
20453  * Originally Released Under LGPL - original licence link has changed is not relivant.
20454  *
20455  * Fork - LGPL
20456  * <script type="text/javascript">
20457  */
20458  
20459 /**
20460  * @class Roo.menu.ColorItem
20461  * @extends Roo.menu.Adapter
20462  * A menu item that wraps the {@link Roo.ColorPalette} component.
20463  * @constructor
20464  * Creates a new ColorItem
20465  * @param {Object} config Configuration options
20466  */
20467 Roo.menu.ColorItem = function(config){
20468     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20469     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20470     this.palette = this.component;
20471     this.relayEvents(this.palette, ["select"]);
20472     if(this.selectHandler){
20473         this.on('select', this.selectHandler, this.scope);
20474     }
20475 };
20476 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20477  * Based on:
20478  * Ext JS Library 1.1.1
20479  * Copyright(c) 2006-2007, Ext JS, LLC.
20480  *
20481  * Originally Released Under LGPL - original licence link has changed is not relivant.
20482  *
20483  * Fork - LGPL
20484  * <script type="text/javascript">
20485  */
20486  
20487
20488 /**
20489  * @class Roo.menu.DateMenu
20490  * @extends Roo.menu.Menu
20491  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20492  * @constructor
20493  * Creates a new DateMenu
20494  * @param {Object} config Configuration options
20495  */
20496 Roo.menu.DateMenu = function(config){
20497     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20498     this.plain = true;
20499     var di = new Roo.menu.DateItem(config);
20500     this.add(di);
20501     /**
20502      * The {@link Roo.DatePicker} instance for this DateMenu
20503      * @type DatePicker
20504      */
20505     this.picker = di.picker;
20506     /**
20507      * @event select
20508      * @param {DatePicker} picker
20509      * @param {Date} date
20510      */
20511     this.relayEvents(di, ["select"]);
20512     this.on('beforeshow', function(){
20513         if(this.picker){
20514             this.picker.hideMonthPicker(false);
20515         }
20516     }, this);
20517 };
20518 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20519     cls:'x-date-menu'
20520 });/*
20521  * Based on:
20522  * Ext JS Library 1.1.1
20523  * Copyright(c) 2006-2007, Ext JS, LLC.
20524  *
20525  * Originally Released Under LGPL - original licence link has changed is not relivant.
20526  *
20527  * Fork - LGPL
20528  * <script type="text/javascript">
20529  */
20530  
20531
20532 /**
20533  * @class Roo.menu.ColorMenu
20534  * @extends Roo.menu.Menu
20535  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20536  * @constructor
20537  * Creates a new ColorMenu
20538  * @param {Object} config Configuration options
20539  */
20540 Roo.menu.ColorMenu = function(config){
20541     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20542     this.plain = true;
20543     var ci = new Roo.menu.ColorItem(config);
20544     this.add(ci);
20545     /**
20546      * The {@link Roo.ColorPalette} instance for this ColorMenu
20547      * @type ColorPalette
20548      */
20549     this.palette = ci.palette;
20550     /**
20551      * @event select
20552      * @param {ColorPalette} palette
20553      * @param {String} color
20554      */
20555     this.relayEvents(ci, ["select"]);
20556 };
20557 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20558  * Based on:
20559  * Ext JS Library 1.1.1
20560  * Copyright(c) 2006-2007, Ext JS, LLC.
20561  *
20562  * Originally Released Under LGPL - original licence link has changed is not relivant.
20563  *
20564  * Fork - LGPL
20565  * <script type="text/javascript">
20566  */
20567  
20568 /**
20569  * @class Roo.form.Field
20570  * @extends Roo.BoxComponent
20571  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20572  * @constructor
20573  * Creates a new Field
20574  * @param {Object} config Configuration options
20575  */
20576 Roo.form.Field = function(config){
20577     Roo.form.Field.superclass.constructor.call(this, config);
20578 };
20579
20580 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20581     /**
20582      * @cfg {String} fieldLabel Label to use when rendering a form.
20583      */
20584        /**
20585      * @cfg {String} qtip Mouse over tip
20586      */
20587      
20588     /**
20589      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20590      */
20591     invalidClass : "x-form-invalid",
20592     /**
20593      * @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")
20594      */
20595     invalidText : "The value in this field is invalid",
20596     /**
20597      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20598      */
20599     focusClass : "x-form-focus",
20600     /**
20601      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20602       automatic validation (defaults to "keyup").
20603      */
20604     validationEvent : "keyup",
20605     /**
20606      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20607      */
20608     validateOnBlur : true,
20609     /**
20610      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20611      */
20612     validationDelay : 250,
20613     /**
20614      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20615      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20616      */
20617     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20618     /**
20619      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20620      */
20621     fieldClass : "x-form-field",
20622     /**
20623      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20624      *<pre>
20625 Value         Description
20626 -----------   ----------------------------------------------------------------------
20627 qtip          Display a quick tip when the user hovers over the field
20628 title         Display a default browser title attribute popup
20629 under         Add a block div beneath the field containing the error text
20630 side          Add an error icon to the right of the field with a popup on hover
20631 [element id]  Add the error text directly to the innerHTML of the specified element
20632 </pre>
20633      */
20634     msgTarget : 'qtip',
20635     /**
20636      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20637      */
20638     msgFx : 'normal',
20639
20640     /**
20641      * @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.
20642      */
20643     readOnly : false,
20644
20645     /**
20646      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20647      */
20648     disabled : false,
20649
20650     /**
20651      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20652      */
20653     inputType : undefined,
20654     
20655     /**
20656      * @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).
20657          */
20658         tabIndex : undefined,
20659         
20660     // private
20661     isFormField : true,
20662
20663     // private
20664     hasFocus : false,
20665     /**
20666      * @property {Roo.Element} fieldEl
20667      * Element Containing the rendered Field (with label etc.)
20668      */
20669     /**
20670      * @cfg {Mixed} value A value to initialize this field with.
20671      */
20672     value : undefined,
20673
20674     /**
20675      * @cfg {String} name The field's HTML name attribute.
20676      */
20677     /**
20678      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20679      */
20680
20681         // private ??
20682         initComponent : function(){
20683         Roo.form.Field.superclass.initComponent.call(this);
20684         this.addEvents({
20685             /**
20686              * @event focus
20687              * Fires when this field receives input focus.
20688              * @param {Roo.form.Field} this
20689              */
20690             focus : true,
20691             /**
20692              * @event blur
20693              * Fires when this field loses input focus.
20694              * @param {Roo.form.Field} this
20695              */
20696             blur : true,
20697             /**
20698              * @event specialkey
20699              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20700              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20701              * @param {Roo.form.Field} this
20702              * @param {Roo.EventObject} e The event object
20703              */
20704             specialkey : true,
20705             /**
20706              * @event change
20707              * Fires just before the field blurs if the field value has changed.
20708              * @param {Roo.form.Field} this
20709              * @param {Mixed} newValue The new value
20710              * @param {Mixed} oldValue The original value
20711              */
20712             change : true,
20713             /**
20714              * @event invalid
20715              * Fires after the field has been marked as invalid.
20716              * @param {Roo.form.Field} this
20717              * @param {String} msg The validation message
20718              */
20719             invalid : true,
20720             /**
20721              * @event valid
20722              * Fires after the field has been validated with no errors.
20723              * @param {Roo.form.Field} this
20724              */
20725             valid : true,
20726              /**
20727              * @event keyup
20728              * Fires after the key up
20729              * @param {Roo.form.Field} this
20730              * @param {Roo.EventObject}  e The event Object
20731              */
20732             keyup : true
20733         });
20734     },
20735
20736     /**
20737      * Returns the name attribute of the field if available
20738      * @return {String} name The field name
20739      */
20740     getName: function(){
20741          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20742     },
20743
20744     // private
20745     onRender : function(ct, position){
20746         Roo.form.Field.superclass.onRender.call(this, ct, position);
20747         if(!this.el){
20748             var cfg = this.getAutoCreate();
20749             if(!cfg.name){
20750                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20751             }
20752             if (!cfg.name.length) {
20753                 delete cfg.name;
20754             }
20755             if(this.inputType){
20756                 cfg.type = this.inputType;
20757             }
20758             this.el = ct.createChild(cfg, position);
20759         }
20760         var type = this.el.dom.type;
20761         if(type){
20762             if(type == 'password'){
20763                 type = 'text';
20764             }
20765             this.el.addClass('x-form-'+type);
20766         }
20767         if(this.readOnly){
20768             this.el.dom.readOnly = true;
20769         }
20770         if(this.tabIndex !== undefined){
20771             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20772         }
20773
20774         this.el.addClass([this.fieldClass, this.cls]);
20775         this.initValue();
20776     },
20777
20778     /**
20779      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20780      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20781      * @return {Roo.form.Field} this
20782      */
20783     applyTo : function(target){
20784         this.allowDomMove = false;
20785         this.el = Roo.get(target);
20786         this.render(this.el.dom.parentNode);
20787         return this;
20788     },
20789
20790     // private
20791     initValue : function(){
20792         if(this.value !== undefined){
20793             this.setValue(this.value);
20794         }else if(this.el.dom.value.length > 0){
20795             this.setValue(this.el.dom.value);
20796         }
20797     },
20798
20799     /**
20800      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20801      */
20802     isDirty : function() {
20803         if(this.disabled) {
20804             return false;
20805         }
20806         return String(this.getValue()) !== String(this.originalValue);
20807     },
20808
20809     // private
20810     afterRender : function(){
20811         Roo.form.Field.superclass.afterRender.call(this);
20812         this.initEvents();
20813     },
20814
20815     // private
20816     fireKey : function(e){
20817         //Roo.log('field ' + e.getKey());
20818         if(e.isNavKeyPress()){
20819             this.fireEvent("specialkey", this, e);
20820         }
20821     },
20822
20823     /**
20824      * Resets the current field value to the originally loaded value and clears any validation messages
20825      */
20826     reset : function(){
20827         this.setValue(this.resetValue);
20828         this.clearInvalid();
20829     },
20830
20831     // private
20832     initEvents : function(){
20833         // safari killled keypress - so keydown is now used..
20834         this.el.on("keydown" , this.fireKey,  this);
20835         this.el.on("focus", this.onFocus,  this);
20836         this.el.on("blur", this.onBlur,  this);
20837         this.el.relayEvent('keyup', this);
20838
20839         // reference to original value for reset
20840         this.originalValue = this.getValue();
20841         this.resetValue =  this.getValue();
20842     },
20843
20844     // private
20845     onFocus : function(){
20846         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20847             this.el.addClass(this.focusClass);
20848         }
20849         if(!this.hasFocus){
20850             this.hasFocus = true;
20851             this.startValue = this.getValue();
20852             this.fireEvent("focus", this);
20853         }
20854     },
20855
20856     beforeBlur : Roo.emptyFn,
20857
20858     // private
20859     onBlur : function(){
20860         this.beforeBlur();
20861         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20862             this.el.removeClass(this.focusClass);
20863         }
20864         this.hasFocus = false;
20865         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20866             this.validate();
20867         }
20868         var v = this.getValue();
20869         if(String(v) !== String(this.startValue)){
20870             this.fireEvent('change', this, v, this.startValue);
20871         }
20872         this.fireEvent("blur", this);
20873     },
20874
20875     /**
20876      * Returns whether or not the field value is currently valid
20877      * @param {Boolean} preventMark True to disable marking the field invalid
20878      * @return {Boolean} True if the value is valid, else false
20879      */
20880     isValid : function(preventMark){
20881         if(this.disabled){
20882             return true;
20883         }
20884         var restore = this.preventMark;
20885         this.preventMark = preventMark === true;
20886         var v = this.validateValue(this.processValue(this.getRawValue()));
20887         this.preventMark = restore;
20888         return v;
20889     },
20890
20891     /**
20892      * Validates the field value
20893      * @return {Boolean} True if the value is valid, else false
20894      */
20895     validate : function(){
20896         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20897             this.clearInvalid();
20898             return true;
20899         }
20900         return false;
20901     },
20902
20903     processValue : function(value){
20904         return value;
20905     },
20906
20907     // private
20908     // Subclasses should provide the validation implementation by overriding this
20909     validateValue : function(value){
20910         return true;
20911     },
20912
20913     /**
20914      * Mark this field as invalid
20915      * @param {String} msg The validation message
20916      */
20917     markInvalid : function(msg){
20918         if(!this.rendered || this.preventMark){ // not rendered
20919             return;
20920         }
20921         
20922         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20923         
20924         obj.el.addClass(this.invalidClass);
20925         msg = msg || this.invalidText;
20926         switch(this.msgTarget){
20927             case 'qtip':
20928                 obj.el.dom.qtip = msg;
20929                 obj.el.dom.qclass = 'x-form-invalid-tip';
20930                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20931                     Roo.QuickTips.enable();
20932                 }
20933                 break;
20934             case 'title':
20935                 this.el.dom.title = msg;
20936                 break;
20937             case 'under':
20938                 if(!this.errorEl){
20939                     var elp = this.el.findParent('.x-form-element', 5, true);
20940                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20941                     this.errorEl.setWidth(elp.getWidth(true)-20);
20942                 }
20943                 this.errorEl.update(msg);
20944                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20945                 break;
20946             case 'side':
20947                 if(!this.errorIcon){
20948                     var elp = this.el.findParent('.x-form-element', 5, true);
20949                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20950                 }
20951                 this.alignErrorIcon();
20952                 this.errorIcon.dom.qtip = msg;
20953                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20954                 this.errorIcon.show();
20955                 this.on('resize', this.alignErrorIcon, this);
20956                 break;
20957             default:
20958                 var t = Roo.getDom(this.msgTarget);
20959                 t.innerHTML = msg;
20960                 t.style.display = this.msgDisplay;
20961                 break;
20962         }
20963         this.fireEvent('invalid', this, msg);
20964     },
20965
20966     // private
20967     alignErrorIcon : function(){
20968         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20969     },
20970
20971     /**
20972      * Clear any invalid styles/messages for this field
20973      */
20974     clearInvalid : function(){
20975         if(!this.rendered || this.preventMark){ // not rendered
20976             return;
20977         }
20978         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20979         
20980         obj.el.removeClass(this.invalidClass);
20981         switch(this.msgTarget){
20982             case 'qtip':
20983                 obj.el.dom.qtip = '';
20984                 break;
20985             case 'title':
20986                 this.el.dom.title = '';
20987                 break;
20988             case 'under':
20989                 if(this.errorEl){
20990                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20991                 }
20992                 break;
20993             case 'side':
20994                 if(this.errorIcon){
20995                     this.errorIcon.dom.qtip = '';
20996                     this.errorIcon.hide();
20997                     this.un('resize', this.alignErrorIcon, this);
20998                 }
20999                 break;
21000             default:
21001                 var t = Roo.getDom(this.msgTarget);
21002                 t.innerHTML = '';
21003                 t.style.display = 'none';
21004                 break;
21005         }
21006         this.fireEvent('valid', this);
21007     },
21008
21009     /**
21010      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21011      * @return {Mixed} value The field value
21012      */
21013     getRawValue : function(){
21014         var v = this.el.getValue();
21015         
21016         return v;
21017     },
21018
21019     /**
21020      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21021      * @return {Mixed} value The field value
21022      */
21023     getValue : function(){
21024         var v = this.el.getValue();
21025          
21026         return v;
21027     },
21028
21029     /**
21030      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21031      * @param {Mixed} value The value to set
21032      */
21033     setRawValue : function(v){
21034         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21035     },
21036
21037     /**
21038      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21039      * @param {Mixed} value The value to set
21040      */
21041     setValue : function(v){
21042         this.value = v;
21043         if(this.rendered){
21044             this.el.dom.value = (v === null || v === undefined ? '' : v);
21045              this.validate();
21046         }
21047     },
21048
21049     adjustSize : function(w, h){
21050         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21051         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21052         return s;
21053     },
21054
21055     adjustWidth : function(tag, w){
21056         tag = tag.toLowerCase();
21057         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21058             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21059                 if(tag == 'input'){
21060                     return w + 2;
21061                 }
21062                 if(tag == 'textarea'){
21063                     return w-2;
21064                 }
21065             }else if(Roo.isOpera){
21066                 if(tag == 'input'){
21067                     return w + 2;
21068                 }
21069                 if(tag == 'textarea'){
21070                     return w-2;
21071                 }
21072             }
21073         }
21074         return w;
21075     }
21076 });
21077
21078
21079 // anything other than normal should be considered experimental
21080 Roo.form.Field.msgFx = {
21081     normal : {
21082         show: function(msgEl, f){
21083             msgEl.setDisplayed('block');
21084         },
21085
21086         hide : function(msgEl, f){
21087             msgEl.setDisplayed(false).update('');
21088         }
21089     },
21090
21091     slide : {
21092         show: function(msgEl, f){
21093             msgEl.slideIn('t', {stopFx:true});
21094         },
21095
21096         hide : function(msgEl, f){
21097             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21098         }
21099     },
21100
21101     slideRight : {
21102         show: function(msgEl, f){
21103             msgEl.fixDisplay();
21104             msgEl.alignTo(f.el, 'tl-tr');
21105             msgEl.slideIn('l', {stopFx:true});
21106         },
21107
21108         hide : function(msgEl, f){
21109             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21110         }
21111     }
21112 };/*
21113  * Based on:
21114  * Ext JS Library 1.1.1
21115  * Copyright(c) 2006-2007, Ext JS, LLC.
21116  *
21117  * Originally Released Under LGPL - original licence link has changed is not relivant.
21118  *
21119  * Fork - LGPL
21120  * <script type="text/javascript">
21121  */
21122  
21123
21124 /**
21125  * @class Roo.form.TextField
21126  * @extends Roo.form.Field
21127  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21128  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21129  * @constructor
21130  * Creates a new TextField
21131  * @param {Object} config Configuration options
21132  */
21133 Roo.form.TextField = function(config){
21134     Roo.form.TextField.superclass.constructor.call(this, config);
21135     this.addEvents({
21136         /**
21137          * @event autosize
21138          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21139          * according to the default logic, but this event provides a hook for the developer to apply additional
21140          * logic at runtime to resize the field if needed.
21141              * @param {Roo.form.Field} this This text field
21142              * @param {Number} width The new field width
21143              */
21144         autosize : true
21145     });
21146 };
21147
21148 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21149     /**
21150      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21151      */
21152     grow : false,
21153     /**
21154      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21155      */
21156     growMin : 30,
21157     /**
21158      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21159      */
21160     growMax : 800,
21161     /**
21162      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21163      */
21164     vtype : null,
21165     /**
21166      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21167      */
21168     maskRe : null,
21169     /**
21170      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21171      */
21172     disableKeyFilter : false,
21173     /**
21174      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21175      */
21176     allowBlank : true,
21177     /**
21178      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21179      */
21180     minLength : 0,
21181     /**
21182      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21183      */
21184     maxLength : Number.MAX_VALUE,
21185     /**
21186      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21187      */
21188     minLengthText : "The minimum length for this field is {0}",
21189     /**
21190      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21191      */
21192     maxLengthText : "The maximum length for this field is {0}",
21193     /**
21194      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21195      */
21196     selectOnFocus : false,
21197     /**
21198      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21199      */
21200     blankText : "This field is required",
21201     /**
21202      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21203      * If available, this function will be called only after the basic validators all return true, and will be passed the
21204      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21205      */
21206     validator : null,
21207     /**
21208      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21209      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21210      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21211      */
21212     regex : null,
21213     /**
21214      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21215      */
21216     regexText : "",
21217     /**
21218      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21219      */
21220     emptyText : null,
21221    
21222
21223     // private
21224     initEvents : function()
21225     {
21226         if (this.emptyText) {
21227             this.el.attr('placeholder', this.emptyText);
21228         }
21229         
21230         Roo.form.TextField.superclass.initEvents.call(this);
21231         if(this.validationEvent == 'keyup'){
21232             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21233             this.el.on('keyup', this.filterValidation, this);
21234         }
21235         else if(this.validationEvent !== false){
21236             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21237         }
21238         
21239         if(this.selectOnFocus){
21240             this.on("focus", this.preFocus, this);
21241             
21242         }
21243         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21244             this.el.on("keypress", this.filterKeys, this);
21245         }
21246         if(this.grow){
21247             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21248             this.el.on("click", this.autoSize,  this);
21249         }
21250         if(this.el.is('input[type=password]') && Roo.isSafari){
21251             this.el.on('keydown', this.SafariOnKeyDown, this);
21252         }
21253     },
21254
21255     processValue : function(value){
21256         if(this.stripCharsRe){
21257             var newValue = value.replace(this.stripCharsRe, '');
21258             if(newValue !== value){
21259                 this.setRawValue(newValue);
21260                 return newValue;
21261             }
21262         }
21263         return value;
21264     },
21265
21266     filterValidation : function(e){
21267         if(!e.isNavKeyPress()){
21268             this.validationTask.delay(this.validationDelay);
21269         }
21270     },
21271
21272     // private
21273     onKeyUp : function(e){
21274         if(!e.isNavKeyPress()){
21275             this.autoSize();
21276         }
21277     },
21278
21279     /**
21280      * Resets the current field value to the originally-loaded value and clears any validation messages.
21281      *  
21282      */
21283     reset : function(){
21284         Roo.form.TextField.superclass.reset.call(this);
21285        
21286     },
21287
21288     
21289     // private
21290     preFocus : function(){
21291         
21292         if(this.selectOnFocus){
21293             this.el.dom.select();
21294         }
21295     },
21296
21297     
21298     // private
21299     filterKeys : function(e){
21300         var k = e.getKey();
21301         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21302             return;
21303         }
21304         var c = e.getCharCode(), cc = String.fromCharCode(c);
21305         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21306             return;
21307         }
21308         if(!this.maskRe.test(cc)){
21309             e.stopEvent();
21310         }
21311     },
21312
21313     setValue : function(v){
21314         
21315         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21316         
21317         this.autoSize();
21318     },
21319
21320     /**
21321      * Validates a value according to the field's validation rules and marks the field as invalid
21322      * if the validation fails
21323      * @param {Mixed} value The value to validate
21324      * @return {Boolean} True if the value is valid, else false
21325      */
21326     validateValue : function(value){
21327         if(value.length < 1)  { // if it's blank
21328              if(this.allowBlank){
21329                 this.clearInvalid();
21330                 return true;
21331              }else{
21332                 this.markInvalid(this.blankText);
21333                 return false;
21334              }
21335         }
21336         if(value.length < this.minLength){
21337             this.markInvalid(String.format(this.minLengthText, this.minLength));
21338             return false;
21339         }
21340         if(value.length > this.maxLength){
21341             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21342             return false;
21343         }
21344         if(this.vtype){
21345             var vt = Roo.form.VTypes;
21346             if(!vt[this.vtype](value, this)){
21347                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21348                 return false;
21349             }
21350         }
21351         if(typeof this.validator == "function"){
21352             var msg = this.validator(value);
21353             if(msg !== true){
21354                 this.markInvalid(msg);
21355                 return false;
21356             }
21357         }
21358         if(this.regex && !this.regex.test(value)){
21359             this.markInvalid(this.regexText);
21360             return false;
21361         }
21362         return true;
21363     },
21364
21365     /**
21366      * Selects text in this field
21367      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21368      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21369      */
21370     selectText : function(start, end){
21371         var v = this.getRawValue();
21372         if(v.length > 0){
21373             start = start === undefined ? 0 : start;
21374             end = end === undefined ? v.length : end;
21375             var d = this.el.dom;
21376             if(d.setSelectionRange){
21377                 d.setSelectionRange(start, end);
21378             }else if(d.createTextRange){
21379                 var range = d.createTextRange();
21380                 range.moveStart("character", start);
21381                 range.moveEnd("character", v.length-end);
21382                 range.select();
21383             }
21384         }
21385     },
21386
21387     /**
21388      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21389      * This only takes effect if grow = true, and fires the autosize event.
21390      */
21391     autoSize : function(){
21392         if(!this.grow || !this.rendered){
21393             return;
21394         }
21395         if(!this.metrics){
21396             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21397         }
21398         var el = this.el;
21399         var v = el.dom.value;
21400         var d = document.createElement('div');
21401         d.appendChild(document.createTextNode(v));
21402         v = d.innerHTML;
21403         d = null;
21404         v += "&#160;";
21405         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21406         this.el.setWidth(w);
21407         this.fireEvent("autosize", this, w);
21408     },
21409     
21410     // private
21411     SafariOnKeyDown : function(event)
21412     {
21413         // this is a workaround for a password hang bug on chrome/ webkit.
21414         
21415         var isSelectAll = false;
21416         
21417         if(this.el.dom.selectionEnd > 0){
21418             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21419         }
21420         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21421             event.preventDefault();
21422             this.setValue('');
21423             return;
21424         }
21425         
21426         if(isSelectAll){ // backspace and delete key
21427             
21428             event.preventDefault();
21429             // this is very hacky as keydown always get's upper case.
21430             //
21431             var cc = String.fromCharCode(event.getCharCode());
21432             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21433             
21434         }
21435         
21436         
21437     }
21438 });/*
21439  * Based on:
21440  * Ext JS Library 1.1.1
21441  * Copyright(c) 2006-2007, Ext JS, LLC.
21442  *
21443  * Originally Released Under LGPL - original licence link has changed is not relivant.
21444  *
21445  * Fork - LGPL
21446  * <script type="text/javascript">
21447  */
21448  
21449 /**
21450  * @class Roo.form.Hidden
21451  * @extends Roo.form.TextField
21452  * Simple Hidden element used on forms 
21453  * 
21454  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21455  * 
21456  * @constructor
21457  * Creates a new Hidden form element.
21458  * @param {Object} config Configuration options
21459  */
21460
21461
21462
21463 // easy hidden field...
21464 Roo.form.Hidden = function(config){
21465     Roo.form.Hidden.superclass.constructor.call(this, config);
21466 };
21467   
21468 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21469     fieldLabel:      '',
21470     inputType:      'hidden',
21471     width:          50,
21472     allowBlank:     true,
21473     labelSeparator: '',
21474     hidden:         true,
21475     itemCls :       'x-form-item-display-none'
21476
21477
21478 });
21479
21480
21481 /*
21482  * Based on:
21483  * Ext JS Library 1.1.1
21484  * Copyright(c) 2006-2007, Ext JS, LLC.
21485  *
21486  * Originally Released Under LGPL - original licence link has changed is not relivant.
21487  *
21488  * Fork - LGPL
21489  * <script type="text/javascript">
21490  */
21491  
21492 /**
21493  * @class Roo.form.TriggerField
21494  * @extends Roo.form.TextField
21495  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21496  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21497  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21498  * for which you can provide a custom implementation.  For example:
21499  * <pre><code>
21500 var trigger = new Roo.form.TriggerField();
21501 trigger.onTriggerClick = myTriggerFn;
21502 trigger.applyTo('my-field');
21503 </code></pre>
21504  *
21505  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21506  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21507  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21508  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21509  * @constructor
21510  * Create a new TriggerField.
21511  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21512  * to the base TextField)
21513  */
21514 Roo.form.TriggerField = function(config){
21515     this.mimicing = false;
21516     Roo.form.TriggerField.superclass.constructor.call(this, config);
21517 };
21518
21519 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21520     /**
21521      * @cfg {String} triggerClass A CSS class to apply to the trigger
21522      */
21523     /**
21524      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21525      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21526      */
21527     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21528     /**
21529      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21530      */
21531     hideTrigger:false,
21532
21533     /** @cfg {Boolean} grow @hide */
21534     /** @cfg {Number} growMin @hide */
21535     /** @cfg {Number} growMax @hide */
21536
21537     /**
21538      * @hide 
21539      * @method
21540      */
21541     autoSize: Roo.emptyFn,
21542     // private
21543     monitorTab : true,
21544     // private
21545     deferHeight : true,
21546
21547     
21548     actionMode : 'wrap',
21549     // private
21550     onResize : function(w, h){
21551         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21552         if(typeof w == 'number'){
21553             var x = w - this.trigger.getWidth();
21554             this.el.setWidth(this.adjustWidth('input', x));
21555             this.trigger.setStyle('left', x+'px');
21556         }
21557     },
21558
21559     // private
21560     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21561
21562     // private
21563     getResizeEl : function(){
21564         return this.wrap;
21565     },
21566
21567     // private
21568     getPositionEl : function(){
21569         return this.wrap;
21570     },
21571
21572     // private
21573     alignErrorIcon : function(){
21574         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21575     },
21576
21577     // private
21578     onRender : function(ct, position){
21579         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21580         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21581         this.trigger = this.wrap.createChild(this.triggerConfig ||
21582                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21583         if(this.hideTrigger){
21584             this.trigger.setDisplayed(false);
21585         }
21586         this.initTrigger();
21587         if(!this.width){
21588             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21589         }
21590     },
21591
21592     // private
21593     initTrigger : function(){
21594         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21595         this.trigger.addClassOnOver('x-form-trigger-over');
21596         this.trigger.addClassOnClick('x-form-trigger-click');
21597     },
21598
21599     // private
21600     onDestroy : function(){
21601         if(this.trigger){
21602             this.trigger.removeAllListeners();
21603             this.trigger.remove();
21604         }
21605         if(this.wrap){
21606             this.wrap.remove();
21607         }
21608         Roo.form.TriggerField.superclass.onDestroy.call(this);
21609     },
21610
21611     // private
21612     onFocus : function(){
21613         Roo.form.TriggerField.superclass.onFocus.call(this);
21614         if(!this.mimicing){
21615             this.wrap.addClass('x-trigger-wrap-focus');
21616             this.mimicing = true;
21617             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21618             if(this.monitorTab){
21619                 this.el.on("keydown", this.checkTab, this);
21620             }
21621         }
21622     },
21623
21624     // private
21625     checkTab : function(e){
21626         if(e.getKey() == e.TAB){
21627             this.triggerBlur();
21628         }
21629     },
21630
21631     // private
21632     onBlur : function(){
21633         // do nothing
21634     },
21635
21636     // private
21637     mimicBlur : function(e, t){
21638         if(!this.wrap.contains(t) && this.validateBlur()){
21639             this.triggerBlur();
21640         }
21641     },
21642
21643     // private
21644     triggerBlur : function(){
21645         this.mimicing = false;
21646         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21647         if(this.monitorTab){
21648             this.el.un("keydown", this.checkTab, this);
21649         }
21650         this.wrap.removeClass('x-trigger-wrap-focus');
21651         Roo.form.TriggerField.superclass.onBlur.call(this);
21652     },
21653
21654     // private
21655     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21656     validateBlur : function(e, t){
21657         return true;
21658     },
21659
21660     // private
21661     onDisable : function(){
21662         Roo.form.TriggerField.superclass.onDisable.call(this);
21663         if(this.wrap){
21664             this.wrap.addClass('x-item-disabled');
21665         }
21666     },
21667
21668     // private
21669     onEnable : function(){
21670         Roo.form.TriggerField.superclass.onEnable.call(this);
21671         if(this.wrap){
21672             this.wrap.removeClass('x-item-disabled');
21673         }
21674     },
21675
21676     // private
21677     onShow : function(){
21678         var ae = this.getActionEl();
21679         
21680         if(ae){
21681             ae.dom.style.display = '';
21682             ae.dom.style.visibility = 'visible';
21683         }
21684     },
21685
21686     // private
21687     
21688     onHide : function(){
21689         var ae = this.getActionEl();
21690         ae.dom.style.display = 'none';
21691     },
21692
21693     /**
21694      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21695      * by an implementing function.
21696      * @method
21697      * @param {EventObject} e
21698      */
21699     onTriggerClick : Roo.emptyFn
21700 });
21701
21702 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21703 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21704 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21705 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21706     initComponent : function(){
21707         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21708
21709         this.triggerConfig = {
21710             tag:'span', cls:'x-form-twin-triggers', cn:[
21711             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21712             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21713         ]};
21714     },
21715
21716     getTrigger : function(index){
21717         return this.triggers[index];
21718     },
21719
21720     initTrigger : function(){
21721         var ts = this.trigger.select('.x-form-trigger', true);
21722         this.wrap.setStyle('overflow', 'hidden');
21723         var triggerField = this;
21724         ts.each(function(t, all, index){
21725             t.hide = function(){
21726                 var w = triggerField.wrap.getWidth();
21727                 this.dom.style.display = 'none';
21728                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21729             };
21730             t.show = function(){
21731                 var w = triggerField.wrap.getWidth();
21732                 this.dom.style.display = '';
21733                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21734             };
21735             var triggerIndex = 'Trigger'+(index+1);
21736
21737             if(this['hide'+triggerIndex]){
21738                 t.dom.style.display = 'none';
21739             }
21740             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21741             t.addClassOnOver('x-form-trigger-over');
21742             t.addClassOnClick('x-form-trigger-click');
21743         }, this);
21744         this.triggers = ts.elements;
21745     },
21746
21747     onTrigger1Click : Roo.emptyFn,
21748     onTrigger2Click : Roo.emptyFn
21749 });/*
21750  * Based on:
21751  * Ext JS Library 1.1.1
21752  * Copyright(c) 2006-2007, Ext JS, LLC.
21753  *
21754  * Originally Released Under LGPL - original licence link has changed is not relivant.
21755  *
21756  * Fork - LGPL
21757  * <script type="text/javascript">
21758  */
21759  
21760 /**
21761  * @class Roo.form.TextArea
21762  * @extends Roo.form.TextField
21763  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21764  * support for auto-sizing.
21765  * @constructor
21766  * Creates a new TextArea
21767  * @param {Object} config Configuration options
21768  */
21769 Roo.form.TextArea = function(config){
21770     Roo.form.TextArea.superclass.constructor.call(this, config);
21771     // these are provided exchanges for backwards compat
21772     // minHeight/maxHeight were replaced by growMin/growMax to be
21773     // compatible with TextField growing config values
21774     if(this.minHeight !== undefined){
21775         this.growMin = this.minHeight;
21776     }
21777     if(this.maxHeight !== undefined){
21778         this.growMax = this.maxHeight;
21779     }
21780 };
21781
21782 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21783     /**
21784      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21785      */
21786     growMin : 60,
21787     /**
21788      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21789      */
21790     growMax: 1000,
21791     /**
21792      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21793      * in the field (equivalent to setting overflow: hidden, defaults to false)
21794      */
21795     preventScrollbars: false,
21796     /**
21797      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21798      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21799      */
21800
21801     // private
21802     onRender : function(ct, position){
21803         if(!this.el){
21804             this.defaultAutoCreate = {
21805                 tag: "textarea",
21806                 style:"width:300px;height:60px;",
21807                 autocomplete: "off"
21808             };
21809         }
21810         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21811         if(this.grow){
21812             this.textSizeEl = Roo.DomHelper.append(document.body, {
21813                 tag: "pre", cls: "x-form-grow-sizer"
21814             });
21815             if(this.preventScrollbars){
21816                 this.el.setStyle("overflow", "hidden");
21817             }
21818             this.el.setHeight(this.growMin);
21819         }
21820     },
21821
21822     onDestroy : function(){
21823         if(this.textSizeEl){
21824             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21825         }
21826         Roo.form.TextArea.superclass.onDestroy.call(this);
21827     },
21828
21829     // private
21830     onKeyUp : function(e){
21831         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21832             this.autoSize();
21833         }
21834     },
21835
21836     /**
21837      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21838      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21839      */
21840     autoSize : function(){
21841         if(!this.grow || !this.textSizeEl){
21842             return;
21843         }
21844         var el = this.el;
21845         var v = el.dom.value;
21846         var ts = this.textSizeEl;
21847
21848         ts.innerHTML = '';
21849         ts.appendChild(document.createTextNode(v));
21850         v = ts.innerHTML;
21851
21852         Roo.fly(ts).setWidth(this.el.getWidth());
21853         if(v.length < 1){
21854             v = "&#160;&#160;";
21855         }else{
21856             if(Roo.isIE){
21857                 v = v.replace(/\n/g, '<p>&#160;</p>');
21858             }
21859             v += "&#160;\n&#160;";
21860         }
21861         ts.innerHTML = v;
21862         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21863         if(h != this.lastHeight){
21864             this.lastHeight = h;
21865             this.el.setHeight(h);
21866             this.fireEvent("autosize", this, h);
21867         }
21868     }
21869 });/*
21870  * Based on:
21871  * Ext JS Library 1.1.1
21872  * Copyright(c) 2006-2007, Ext JS, LLC.
21873  *
21874  * Originally Released Under LGPL - original licence link has changed is not relivant.
21875  *
21876  * Fork - LGPL
21877  * <script type="text/javascript">
21878  */
21879  
21880
21881 /**
21882  * @class Roo.form.NumberField
21883  * @extends Roo.form.TextField
21884  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21885  * @constructor
21886  * Creates a new NumberField
21887  * @param {Object} config Configuration options
21888  */
21889 Roo.form.NumberField = function(config){
21890     Roo.form.NumberField.superclass.constructor.call(this, config);
21891 };
21892
21893 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21894     /**
21895      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21896      */
21897     fieldClass: "x-form-field x-form-num-field",
21898     /**
21899      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21900      */
21901     allowDecimals : true,
21902     /**
21903      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21904      */
21905     decimalSeparator : ".",
21906     /**
21907      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21908      */
21909     decimalPrecision : 2,
21910     /**
21911      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21912      */
21913     allowNegative : true,
21914     /**
21915      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21916      */
21917     minValue : Number.NEGATIVE_INFINITY,
21918     /**
21919      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21920      */
21921     maxValue : Number.MAX_VALUE,
21922     /**
21923      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21924      */
21925     minText : "The minimum value for this field is {0}",
21926     /**
21927      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21928      */
21929     maxText : "The maximum value for this field is {0}",
21930     /**
21931      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21932      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21933      */
21934     nanText : "{0} is not a valid number",
21935
21936     // private
21937     initEvents : function(){
21938         Roo.form.NumberField.superclass.initEvents.call(this);
21939         var allowed = "0123456789";
21940         if(this.allowDecimals){
21941             allowed += this.decimalSeparator;
21942         }
21943         if(this.allowNegative){
21944             allowed += "-";
21945         }
21946         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21947         var keyPress = function(e){
21948             var k = e.getKey();
21949             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21950                 return;
21951             }
21952             var c = e.getCharCode();
21953             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21954                 e.stopEvent();
21955             }
21956         };
21957         this.el.on("keypress", keyPress, this);
21958     },
21959
21960     // private
21961     validateValue : function(value){
21962         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21963             return false;
21964         }
21965         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21966              return true;
21967         }
21968         var num = this.parseValue(value);
21969         if(isNaN(num)){
21970             this.markInvalid(String.format(this.nanText, value));
21971             return false;
21972         }
21973         if(num < this.minValue){
21974             this.markInvalid(String.format(this.minText, this.minValue));
21975             return false;
21976         }
21977         if(num > this.maxValue){
21978             this.markInvalid(String.format(this.maxText, this.maxValue));
21979             return false;
21980         }
21981         return true;
21982     },
21983
21984     getValue : function(){
21985         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21986     },
21987
21988     // private
21989     parseValue : function(value){
21990         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21991         return isNaN(value) ? '' : value;
21992     },
21993
21994     // private
21995     fixPrecision : function(value){
21996         var nan = isNaN(value);
21997         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21998             return nan ? '' : value;
21999         }
22000         return parseFloat(value).toFixed(this.decimalPrecision);
22001     },
22002
22003     setValue : function(v){
22004         v = this.fixPrecision(v);
22005         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22006     },
22007
22008     // private
22009     decimalPrecisionFcn : function(v){
22010         return Math.floor(v);
22011     },
22012
22013     beforeBlur : function(){
22014         var v = this.parseValue(this.getRawValue());
22015         if(v){
22016             this.setValue(v);
22017         }
22018     }
22019 });/*
22020  * Based on:
22021  * Ext JS Library 1.1.1
22022  * Copyright(c) 2006-2007, Ext JS, LLC.
22023  *
22024  * Originally Released Under LGPL - original licence link has changed is not relivant.
22025  *
22026  * Fork - LGPL
22027  * <script type="text/javascript">
22028  */
22029  
22030 /**
22031  * @class Roo.form.DateField
22032  * @extends Roo.form.TriggerField
22033  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22034 * @constructor
22035 * Create a new DateField
22036 * @param {Object} config
22037  */
22038 Roo.form.DateField = function(config){
22039     Roo.form.DateField.superclass.constructor.call(this, config);
22040     
22041       this.addEvents({
22042          
22043         /**
22044          * @event select
22045          * Fires when a date is selected
22046              * @param {Roo.form.DateField} combo This combo box
22047              * @param {Date} date The date selected
22048              */
22049         'select' : true
22050          
22051     });
22052     
22053     
22054     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22055     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22056     this.ddMatch = null;
22057     if(this.disabledDates){
22058         var dd = this.disabledDates;
22059         var re = "(?:";
22060         for(var i = 0; i < dd.length; i++){
22061             re += dd[i];
22062             if(i != dd.length-1) re += "|";
22063         }
22064         this.ddMatch = new RegExp(re + ")");
22065     }
22066 };
22067
22068 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22069     /**
22070      * @cfg {String} format
22071      * The default date format string which can be overriden for localization support.  The format must be
22072      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22073      */
22074     format : "m/d/y",
22075     /**
22076      * @cfg {String} altFormats
22077      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22078      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22079      */
22080     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22081     /**
22082      * @cfg {Array} disabledDays
22083      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22084      */
22085     disabledDays : null,
22086     /**
22087      * @cfg {String} disabledDaysText
22088      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22089      */
22090     disabledDaysText : "Disabled",
22091     /**
22092      * @cfg {Array} disabledDates
22093      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22094      * expression so they are very powerful. Some examples:
22095      * <ul>
22096      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22097      * <li>["03/08", "09/16"] would disable those days for every year</li>
22098      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22099      * <li>["03/../2006"] would disable every day in March 2006</li>
22100      * <li>["^03"] would disable every day in every March</li>
22101      * </ul>
22102      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22103      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22104      */
22105     disabledDates : null,
22106     /**
22107      * @cfg {String} disabledDatesText
22108      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22109      */
22110     disabledDatesText : "Disabled",
22111     /**
22112      * @cfg {Date/String} minValue
22113      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22114      * valid format (defaults to null).
22115      */
22116     minValue : null,
22117     /**
22118      * @cfg {Date/String} maxValue
22119      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22120      * valid format (defaults to null).
22121      */
22122     maxValue : null,
22123     /**
22124      * @cfg {String} minText
22125      * The error text to display when the date in the cell is before minValue (defaults to
22126      * 'The date in this field must be after {minValue}').
22127      */
22128     minText : "The date in this field must be equal to or after {0}",
22129     /**
22130      * @cfg {String} maxText
22131      * The error text to display when the date in the cell is after maxValue (defaults to
22132      * 'The date in this field must be before {maxValue}').
22133      */
22134     maxText : "The date in this field must be equal to or before {0}",
22135     /**
22136      * @cfg {String} invalidText
22137      * The error text to display when the date in the field is invalid (defaults to
22138      * '{value} is not a valid date - it must be in the format {format}').
22139      */
22140     invalidText : "{0} is not a valid date - it must be in the format {1}",
22141     /**
22142      * @cfg {String} triggerClass
22143      * An additional CSS class used to style the trigger button.  The trigger will always get the
22144      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22145      * which displays a calendar icon).
22146      */
22147     triggerClass : 'x-form-date-trigger',
22148     
22149
22150     /**
22151      * @cfg {Boolean} useIso
22152      * if enabled, then the date field will use a hidden field to store the 
22153      * real value as iso formated date. default (false)
22154      */ 
22155     useIso : false,
22156     /**
22157      * @cfg {String/Object} autoCreate
22158      * A DomHelper element spec, or true for a default element spec (defaults to
22159      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22160      */ 
22161     // private
22162     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22163     
22164     // private
22165     hiddenField: false,
22166     
22167     onRender : function(ct, position)
22168     {
22169         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22170         if (this.useIso) {
22171             //this.el.dom.removeAttribute('name'); 
22172             Roo.log("Changing name?");
22173             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22174             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22175                     'before', true);
22176             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22177             // prevent input submission
22178             this.hiddenName = this.name;
22179         }
22180             
22181             
22182     },
22183     
22184     // private
22185     validateValue : function(value)
22186     {
22187         value = this.formatDate(value);
22188         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22189             Roo.log('super failed');
22190             return false;
22191         }
22192         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22193              return true;
22194         }
22195         var svalue = value;
22196         value = this.parseDate(value);
22197         if(!value){
22198             Roo.log('parse date failed' + svalue);
22199             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22200             return false;
22201         }
22202         var time = value.getTime();
22203         if(this.minValue && time < this.minValue.getTime()){
22204             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22205             return false;
22206         }
22207         if(this.maxValue && time > this.maxValue.getTime()){
22208             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22209             return false;
22210         }
22211         if(this.disabledDays){
22212             var day = value.getDay();
22213             for(var i = 0; i < this.disabledDays.length; i++) {
22214                 if(day === this.disabledDays[i]){
22215                     this.markInvalid(this.disabledDaysText);
22216                     return false;
22217                 }
22218             }
22219         }
22220         var fvalue = this.formatDate(value);
22221         if(this.ddMatch && this.ddMatch.test(fvalue)){
22222             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22223             return false;
22224         }
22225         return true;
22226     },
22227
22228     // private
22229     // Provides logic to override the default TriggerField.validateBlur which just returns true
22230     validateBlur : function(){
22231         return !this.menu || !this.menu.isVisible();
22232     },
22233     
22234     getName: function()
22235     {
22236         // returns hidden if it's set..
22237         if (!this.rendered) {return ''};
22238         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22239         
22240     },
22241
22242     /**
22243      * Returns the current date value of the date field.
22244      * @return {Date} The date value
22245      */
22246     getValue : function(){
22247         
22248         return  this.hiddenField ?
22249                 this.hiddenField.value :
22250                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22251     },
22252
22253     /**
22254      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22255      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22256      * (the default format used is "m/d/y").
22257      * <br />Usage:
22258      * <pre><code>
22259 //All of these calls set the same date value (May 4, 2006)
22260
22261 //Pass a date object:
22262 var dt = new Date('5/4/06');
22263 dateField.setValue(dt);
22264
22265 //Pass a date string (default format):
22266 dateField.setValue('5/4/06');
22267
22268 //Pass a date string (custom format):
22269 dateField.format = 'Y-m-d';
22270 dateField.setValue('2006-5-4');
22271 </code></pre>
22272      * @param {String/Date} date The date or valid date string
22273      */
22274     setValue : function(date){
22275         if (this.hiddenField) {
22276             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22277         }
22278         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22279         // make sure the value field is always stored as a date..
22280         this.value = this.parseDate(date);
22281         
22282         
22283     },
22284
22285     // private
22286     parseDate : function(value){
22287         if(!value || value instanceof Date){
22288             return value;
22289         }
22290         var v = Date.parseDate(value, this.format);
22291          if (!v && this.useIso) {
22292             v = Date.parseDate(value, 'Y-m-d');
22293         }
22294         if(!v && this.altFormats){
22295             if(!this.altFormatsArray){
22296                 this.altFormatsArray = this.altFormats.split("|");
22297             }
22298             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22299                 v = Date.parseDate(value, this.altFormatsArray[i]);
22300             }
22301         }
22302         return v;
22303     },
22304
22305     // private
22306     formatDate : function(date, fmt){
22307         return (!date || !(date instanceof Date)) ?
22308                date : date.dateFormat(fmt || this.format);
22309     },
22310
22311     // private
22312     menuListeners : {
22313         select: function(m, d){
22314             
22315             this.setValue(d);
22316             this.fireEvent('select', this, d);
22317         },
22318         show : function(){ // retain focus styling
22319             this.onFocus();
22320         },
22321         hide : function(){
22322             this.focus.defer(10, this);
22323             var ml = this.menuListeners;
22324             this.menu.un("select", ml.select,  this);
22325             this.menu.un("show", ml.show,  this);
22326             this.menu.un("hide", ml.hide,  this);
22327         }
22328     },
22329
22330     // private
22331     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22332     onTriggerClick : function(){
22333         if(this.disabled){
22334             return;
22335         }
22336         if(this.menu == null){
22337             this.menu = new Roo.menu.DateMenu();
22338         }
22339         Roo.apply(this.menu.picker,  {
22340             showClear: this.allowBlank,
22341             minDate : this.minValue,
22342             maxDate : this.maxValue,
22343             disabledDatesRE : this.ddMatch,
22344             disabledDatesText : this.disabledDatesText,
22345             disabledDays : this.disabledDays,
22346             disabledDaysText : this.disabledDaysText,
22347             format : this.useIso ? 'Y-m-d' : this.format,
22348             minText : String.format(this.minText, this.formatDate(this.minValue)),
22349             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22350         });
22351         this.menu.on(Roo.apply({}, this.menuListeners, {
22352             scope:this
22353         }));
22354         this.menu.picker.setValue(this.getValue() || new Date());
22355         this.menu.show(this.el, "tl-bl?");
22356     },
22357
22358     beforeBlur : function(){
22359         var v = this.parseDate(this.getRawValue());
22360         if(v){
22361             this.setValue(v);
22362         }
22363     },
22364
22365     /*@
22366      * overide
22367      * 
22368      */
22369     isDirty : function() {
22370         if(this.disabled) {
22371             return false;
22372         }
22373         
22374         if(typeof(this.startValue) === 'undefined'){
22375             return false;
22376         }
22377         
22378         return String(this.getValue()) !== String(this.startValue);
22379         
22380     }
22381 });/*
22382  * Based on:
22383  * Ext JS Library 1.1.1
22384  * Copyright(c) 2006-2007, Ext JS, LLC.
22385  *
22386  * Originally Released Under LGPL - original licence link has changed is not relivant.
22387  *
22388  * Fork - LGPL
22389  * <script type="text/javascript">
22390  */
22391  
22392 /**
22393  * @class Roo.form.MonthField
22394  * @extends Roo.form.TriggerField
22395  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22396 * @constructor
22397 * Create a new MonthField
22398 * @param {Object} config
22399  */
22400 Roo.form.MonthField = function(config){
22401     
22402     Roo.form.MonthField.superclass.constructor.call(this, config);
22403     
22404       this.addEvents({
22405          
22406         /**
22407          * @event select
22408          * Fires when a date is selected
22409              * @param {Roo.form.MonthFieeld} combo This combo box
22410              * @param {Date} date The date selected
22411              */
22412         'select' : true
22413          
22414     });
22415     
22416     
22417     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22418     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22419     this.ddMatch = null;
22420     if(this.disabledDates){
22421         var dd = this.disabledDates;
22422         var re = "(?:";
22423         for(var i = 0; i < dd.length; i++){
22424             re += dd[i];
22425             if(i != dd.length-1) re += "|";
22426         }
22427         this.ddMatch = new RegExp(re + ")");
22428     }
22429 };
22430
22431 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22432     /**
22433      * @cfg {String} format
22434      * The default date format string which can be overriden for localization support.  The format must be
22435      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22436      */
22437     format : "M Y",
22438     /**
22439      * @cfg {String} altFormats
22440      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22441      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22442      */
22443     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22444     /**
22445      * @cfg {Array} disabledDays
22446      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22447      */
22448     disabledDays : [0,1,2,3,4,5,6],
22449     /**
22450      * @cfg {String} disabledDaysText
22451      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22452      */
22453     disabledDaysText : "Disabled",
22454     /**
22455      * @cfg {Array} disabledDates
22456      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22457      * expression so they are very powerful. Some examples:
22458      * <ul>
22459      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22460      * <li>["03/08", "09/16"] would disable those days for every year</li>
22461      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22462      * <li>["03/../2006"] would disable every day in March 2006</li>
22463      * <li>["^03"] would disable every day in every March</li>
22464      * </ul>
22465      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22466      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22467      */
22468     disabledDates : null,
22469     /**
22470      * @cfg {String} disabledDatesText
22471      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22472      */
22473     disabledDatesText : "Disabled",
22474     /**
22475      * @cfg {Date/String} minValue
22476      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22477      * valid format (defaults to null).
22478      */
22479     minValue : null,
22480     /**
22481      * @cfg {Date/String} maxValue
22482      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22483      * valid format (defaults to null).
22484      */
22485     maxValue : null,
22486     /**
22487      * @cfg {String} minText
22488      * The error text to display when the date in the cell is before minValue (defaults to
22489      * 'The date in this field must be after {minValue}').
22490      */
22491     minText : "The date in this field must be equal to or after {0}",
22492     /**
22493      * @cfg {String} maxTextf
22494      * The error text to display when the date in the cell is after maxValue (defaults to
22495      * 'The date in this field must be before {maxValue}').
22496      */
22497     maxText : "The date in this field must be equal to or before {0}",
22498     /**
22499      * @cfg {String} invalidText
22500      * The error text to display when the date in the field is invalid (defaults to
22501      * '{value} is not a valid date - it must be in the format {format}').
22502      */
22503     invalidText : "{0} is not a valid date - it must be in the format {1}",
22504     /**
22505      * @cfg {String} triggerClass
22506      * An additional CSS class used to style the trigger button.  The trigger will always get the
22507      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22508      * which displays a calendar icon).
22509      */
22510     triggerClass : 'x-form-date-trigger',
22511     
22512
22513     /**
22514      * @cfg {Boolean} useIso
22515      * if enabled, then the date field will use a hidden field to store the 
22516      * real value as iso formated date. default (true)
22517      */ 
22518     useIso : true,
22519     /**
22520      * @cfg {String/Object} autoCreate
22521      * A DomHelper element spec, or true for a default element spec (defaults to
22522      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22523      */ 
22524     // private
22525     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22526     
22527     // private
22528     hiddenField: false,
22529     
22530     hideMonthPicker : false,
22531     
22532     onRender : function(ct, position)
22533     {
22534         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22535         if (this.useIso) {
22536             this.el.dom.removeAttribute('name'); 
22537             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22538                     'before', true);
22539             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22540             // prevent input submission
22541             this.hiddenName = this.name;
22542         }
22543             
22544             
22545     },
22546     
22547     // private
22548     validateValue : function(value)
22549     {
22550         value = this.formatDate(value);
22551         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22552             return false;
22553         }
22554         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22555              return true;
22556         }
22557         var svalue = value;
22558         value = this.parseDate(value);
22559         if(!value){
22560             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22561             return false;
22562         }
22563         var time = value.getTime();
22564         if(this.minValue && time < this.minValue.getTime()){
22565             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22566             return false;
22567         }
22568         if(this.maxValue && time > this.maxValue.getTime()){
22569             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22570             return false;
22571         }
22572         /*if(this.disabledDays){
22573             var day = value.getDay();
22574             for(var i = 0; i < this.disabledDays.length; i++) {
22575                 if(day === this.disabledDays[i]){
22576                     this.markInvalid(this.disabledDaysText);
22577                     return false;
22578                 }
22579             }
22580         }
22581         */
22582         var fvalue = this.formatDate(value);
22583         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22584             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22585             return false;
22586         }
22587         */
22588         return true;
22589     },
22590
22591     // private
22592     // Provides logic to override the default TriggerField.validateBlur which just returns true
22593     validateBlur : function(){
22594         return !this.menu || !this.menu.isVisible();
22595     },
22596
22597     /**
22598      * Returns the current date value of the date field.
22599      * @return {Date} The date value
22600      */
22601     getValue : function(){
22602         
22603         
22604         
22605         return  this.hiddenField ?
22606                 this.hiddenField.value :
22607                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22608     },
22609
22610     /**
22611      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22612      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22613      * (the default format used is "m/d/y").
22614      * <br />Usage:
22615      * <pre><code>
22616 //All of these calls set the same date value (May 4, 2006)
22617
22618 //Pass a date object:
22619 var dt = new Date('5/4/06');
22620 monthField.setValue(dt);
22621
22622 //Pass a date string (default format):
22623 monthField.setValue('5/4/06');
22624
22625 //Pass a date string (custom format):
22626 monthField.format = 'Y-m-d';
22627 monthField.setValue('2006-5-4');
22628 </code></pre>
22629      * @param {String/Date} date The date or valid date string
22630      */
22631     setValue : function(date){
22632         Roo.log('month setValue' + date);
22633         // can only be first of month..
22634         
22635         var val = this.parseDate(date);
22636         
22637         if (this.hiddenField) {
22638             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22639         }
22640         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22641         this.value = this.parseDate(date);
22642     },
22643
22644     // private
22645     parseDate : function(value){
22646         if(!value || value instanceof Date){
22647             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22648             return value;
22649         }
22650         var v = Date.parseDate(value, this.format);
22651         if (!v && this.useIso) {
22652             v = Date.parseDate(value, 'Y-m-d');
22653         }
22654         if (v) {
22655             // 
22656             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22657         }
22658         
22659         
22660         if(!v && this.altFormats){
22661             if(!this.altFormatsArray){
22662                 this.altFormatsArray = this.altFormats.split("|");
22663             }
22664             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22665                 v = Date.parseDate(value, this.altFormatsArray[i]);
22666             }
22667         }
22668         return v;
22669     },
22670
22671     // private
22672     formatDate : function(date, fmt){
22673         return (!date || !(date instanceof Date)) ?
22674                date : date.dateFormat(fmt || this.format);
22675     },
22676
22677     // private
22678     menuListeners : {
22679         select: function(m, d){
22680             this.setValue(d);
22681             this.fireEvent('select', this, d);
22682         },
22683         show : function(){ // retain focus styling
22684             this.onFocus();
22685         },
22686         hide : function(){
22687             this.focus.defer(10, this);
22688             var ml = this.menuListeners;
22689             this.menu.un("select", ml.select,  this);
22690             this.menu.un("show", ml.show,  this);
22691             this.menu.un("hide", ml.hide,  this);
22692         }
22693     },
22694     // private
22695     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22696     onTriggerClick : function(){
22697         if(this.disabled){
22698             return;
22699         }
22700         if(this.menu == null){
22701             this.menu = new Roo.menu.DateMenu();
22702            
22703         }
22704         
22705         Roo.apply(this.menu.picker,  {
22706             
22707             showClear: this.allowBlank,
22708             minDate : this.minValue,
22709             maxDate : this.maxValue,
22710             disabledDatesRE : this.ddMatch,
22711             disabledDatesText : this.disabledDatesText,
22712             
22713             format : this.useIso ? 'Y-m-d' : this.format,
22714             minText : String.format(this.minText, this.formatDate(this.minValue)),
22715             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22716             
22717         });
22718          this.menu.on(Roo.apply({}, this.menuListeners, {
22719             scope:this
22720         }));
22721        
22722         
22723         var m = this.menu;
22724         var p = m.picker;
22725         
22726         // hide month picker get's called when we called by 'before hide';
22727         
22728         var ignorehide = true;
22729         p.hideMonthPicker  = function(disableAnim){
22730             if (ignorehide) {
22731                 return;
22732             }
22733              if(this.monthPicker){
22734                 Roo.log("hideMonthPicker called");
22735                 if(disableAnim === true){
22736                     this.monthPicker.hide();
22737                 }else{
22738                     this.monthPicker.slideOut('t', {duration:.2});
22739                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22740                     p.fireEvent("select", this, this.value);
22741                     m.hide();
22742                 }
22743             }
22744         }
22745         
22746         Roo.log('picker set value');
22747         Roo.log(this.getValue());
22748         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22749         m.show(this.el, 'tl-bl?');
22750         ignorehide  = false;
22751         // this will trigger hideMonthPicker..
22752         
22753         
22754         // hidden the day picker
22755         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22756         
22757         
22758         
22759       
22760         
22761         p.showMonthPicker.defer(100, p);
22762     
22763         
22764        
22765     },
22766
22767     beforeBlur : function(){
22768         var v = this.parseDate(this.getRawValue());
22769         if(v){
22770             this.setValue(v);
22771         }
22772     }
22773
22774     /** @cfg {Boolean} grow @hide */
22775     /** @cfg {Number} growMin @hide */
22776     /** @cfg {Number} growMax @hide */
22777     /**
22778      * @hide
22779      * @method autoSize
22780      */
22781 });/*
22782  * Based on:
22783  * Ext JS Library 1.1.1
22784  * Copyright(c) 2006-2007, Ext JS, LLC.
22785  *
22786  * Originally Released Under LGPL - original licence link has changed is not relivant.
22787  *
22788  * Fork - LGPL
22789  * <script type="text/javascript">
22790  */
22791  
22792
22793 /**
22794  * @class Roo.form.ComboBox
22795  * @extends Roo.form.TriggerField
22796  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22797  * @constructor
22798  * Create a new ComboBox.
22799  * @param {Object} config Configuration options
22800  */
22801 Roo.form.ComboBox = function(config){
22802     Roo.form.ComboBox.superclass.constructor.call(this, config);
22803     this.addEvents({
22804         /**
22805          * @event expand
22806          * Fires when the dropdown list is expanded
22807              * @param {Roo.form.ComboBox} combo This combo box
22808              */
22809         'expand' : true,
22810         /**
22811          * @event collapse
22812          * Fires when the dropdown list is collapsed
22813              * @param {Roo.form.ComboBox} combo This combo box
22814              */
22815         'collapse' : true,
22816         /**
22817          * @event beforeselect
22818          * Fires before a list item is selected. Return false to cancel the selection.
22819              * @param {Roo.form.ComboBox} combo This combo box
22820              * @param {Roo.data.Record} record The data record returned from the underlying store
22821              * @param {Number} index The index of the selected item in the dropdown list
22822              */
22823         'beforeselect' : true,
22824         /**
22825          * @event select
22826          * Fires when a list item is selected
22827              * @param {Roo.form.ComboBox} combo This combo box
22828              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22829              * @param {Number} index The index of the selected item in the dropdown list
22830              */
22831         'select' : true,
22832         /**
22833          * @event beforequery
22834          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22835          * The event object passed has these properties:
22836              * @param {Roo.form.ComboBox} combo This combo box
22837              * @param {String} query The query
22838              * @param {Boolean} forceAll true to force "all" query
22839              * @param {Boolean} cancel true to cancel the query
22840              * @param {Object} e The query event object
22841              */
22842         'beforequery': true,
22843          /**
22844          * @event add
22845          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22846              * @param {Roo.form.ComboBox} combo This combo box
22847              */
22848         'add' : true,
22849         /**
22850          * @event edit
22851          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22852              * @param {Roo.form.ComboBox} combo This combo box
22853              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22854              */
22855         'edit' : true
22856         
22857         
22858     });
22859     if(this.transform){
22860         this.allowDomMove = false;
22861         var s = Roo.getDom(this.transform);
22862         if(!this.hiddenName){
22863             this.hiddenName = s.name;
22864         }
22865         if(!this.store){
22866             this.mode = 'local';
22867             var d = [], opts = s.options;
22868             for(var i = 0, len = opts.length;i < len; i++){
22869                 var o = opts[i];
22870                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22871                 if(o.selected) {
22872                     this.value = value;
22873                 }
22874                 d.push([value, o.text]);
22875             }
22876             this.store = new Roo.data.SimpleStore({
22877                 'id': 0,
22878                 fields: ['value', 'text'],
22879                 data : d
22880             });
22881             this.valueField = 'value';
22882             this.displayField = 'text';
22883         }
22884         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22885         if(!this.lazyRender){
22886             this.target = true;
22887             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22888             s.parentNode.removeChild(s); // remove it
22889             this.render(this.el.parentNode);
22890         }else{
22891             s.parentNode.removeChild(s); // remove it
22892         }
22893
22894     }
22895     if (this.store) {
22896         this.store = Roo.factory(this.store, Roo.data);
22897     }
22898     
22899     this.selectedIndex = -1;
22900     if(this.mode == 'local'){
22901         if(config.queryDelay === undefined){
22902             this.queryDelay = 10;
22903         }
22904         if(config.minChars === undefined){
22905             this.minChars = 0;
22906         }
22907     }
22908 };
22909
22910 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22911     /**
22912      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22913      */
22914     /**
22915      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22916      * rendering into an Roo.Editor, defaults to false)
22917      */
22918     /**
22919      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22920      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22921      */
22922     /**
22923      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22924      */
22925     /**
22926      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22927      * the dropdown list (defaults to undefined, with no header element)
22928      */
22929
22930      /**
22931      * @cfg {String/Roo.Template} tpl The template to use to render the output
22932      */
22933      
22934     // private
22935     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22936     /**
22937      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22938      */
22939     listWidth: undefined,
22940     /**
22941      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22942      * mode = 'remote' or 'text' if mode = 'local')
22943      */
22944     displayField: undefined,
22945     /**
22946      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22947      * mode = 'remote' or 'value' if mode = 'local'). 
22948      * Note: use of a valueField requires the user make a selection
22949      * in order for a value to be mapped.
22950      */
22951     valueField: undefined,
22952     
22953     
22954     /**
22955      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22956      * field's data value (defaults to the underlying DOM element's name)
22957      */
22958     hiddenName: undefined,
22959     /**
22960      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22961      */
22962     listClass: '',
22963     /**
22964      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22965      */
22966     selectedClass: 'x-combo-selected',
22967     /**
22968      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22969      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22970      * which displays a downward arrow icon).
22971      */
22972     triggerClass : 'x-form-arrow-trigger',
22973     /**
22974      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22975      */
22976     shadow:'sides',
22977     /**
22978      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22979      * anchor positions (defaults to 'tl-bl')
22980      */
22981     listAlign: 'tl-bl?',
22982     /**
22983      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22984      */
22985     maxHeight: 300,
22986     /**
22987      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22988      * query specified by the allQuery config option (defaults to 'query')
22989      */
22990     triggerAction: 'query',
22991     /**
22992      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22993      * (defaults to 4, does not apply if editable = false)
22994      */
22995     minChars : 4,
22996     /**
22997      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22998      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22999      */
23000     typeAhead: false,
23001     /**
23002      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23003      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23004      */
23005     queryDelay: 500,
23006     /**
23007      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23008      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23009      */
23010     pageSize: 0,
23011     /**
23012      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23013      * when editable = true (defaults to false)
23014      */
23015     selectOnFocus:false,
23016     /**
23017      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23018      */
23019     queryParam: 'query',
23020     /**
23021      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23022      * when mode = 'remote' (defaults to 'Loading...')
23023      */
23024     loadingText: 'Loading...',
23025     /**
23026      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23027      */
23028     resizable: false,
23029     /**
23030      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23031      */
23032     handleHeight : 8,
23033     /**
23034      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23035      * traditional select (defaults to true)
23036      */
23037     editable: true,
23038     /**
23039      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23040      */
23041     allQuery: '',
23042     /**
23043      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23044      */
23045     mode: 'remote',
23046     /**
23047      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23048      * listWidth has a higher value)
23049      */
23050     minListWidth : 70,
23051     /**
23052      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23053      * allow the user to set arbitrary text into the field (defaults to false)
23054      */
23055     forceSelection:false,
23056     /**
23057      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23058      * if typeAhead = true (defaults to 250)
23059      */
23060     typeAheadDelay : 250,
23061     /**
23062      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23063      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23064      */
23065     valueNotFoundText : undefined,
23066     /**
23067      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23068      */
23069     blockFocus : false,
23070     
23071     /**
23072      * @cfg {Boolean} disableClear Disable showing of clear button.
23073      */
23074     disableClear : false,
23075     /**
23076      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23077      */
23078     alwaysQuery : false,
23079     
23080     //private
23081     addicon : false,
23082     editicon: false,
23083     
23084     // element that contains real text value.. (when hidden is used..)
23085      
23086     // private
23087     onRender : function(ct, position){
23088         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23089         if(this.hiddenName){
23090             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23091                     'before', true);
23092             this.hiddenField.value =
23093                 this.hiddenValue !== undefined ? this.hiddenValue :
23094                 this.value !== undefined ? this.value : '';
23095
23096             // prevent input submission
23097             this.el.dom.removeAttribute('name');
23098              
23099              
23100         }
23101         if(Roo.isGecko){
23102             this.el.dom.setAttribute('autocomplete', 'off');
23103         }
23104
23105         var cls = 'x-combo-list';
23106
23107         this.list = new Roo.Layer({
23108             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23109         });
23110
23111         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23112         this.list.setWidth(lw);
23113         this.list.swallowEvent('mousewheel');
23114         this.assetHeight = 0;
23115
23116         if(this.title){
23117             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23118             this.assetHeight += this.header.getHeight();
23119         }
23120
23121         this.innerList = this.list.createChild({cls:cls+'-inner'});
23122         this.innerList.on('mouseover', this.onViewOver, this);
23123         this.innerList.on('mousemove', this.onViewMove, this);
23124         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23125         
23126         if(this.allowBlank && !this.pageSize && !this.disableClear){
23127             this.footer = this.list.createChild({cls:cls+'-ft'});
23128             this.pageTb = new Roo.Toolbar(this.footer);
23129            
23130         }
23131         if(this.pageSize){
23132             this.footer = this.list.createChild({cls:cls+'-ft'});
23133             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23134                     {pageSize: this.pageSize});
23135             
23136         }
23137         
23138         if (this.pageTb && this.allowBlank && !this.disableClear) {
23139             var _this = this;
23140             this.pageTb.add(new Roo.Toolbar.Fill(), {
23141                 cls: 'x-btn-icon x-btn-clear',
23142                 text: '&#160;',
23143                 handler: function()
23144                 {
23145                     _this.collapse();
23146                     _this.clearValue();
23147                     _this.onSelect(false, -1);
23148                 }
23149             });
23150         }
23151         if (this.footer) {
23152             this.assetHeight += this.footer.getHeight();
23153         }
23154         
23155
23156         if(!this.tpl){
23157             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23158         }
23159
23160         this.view = new Roo.View(this.innerList, this.tpl, {
23161             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23162         });
23163
23164         this.view.on('click', this.onViewClick, this);
23165
23166         this.store.on('beforeload', this.onBeforeLoad, this);
23167         this.store.on('load', this.onLoad, this);
23168         this.store.on('loadexception', this.onLoadException, this);
23169
23170         if(this.resizable){
23171             this.resizer = new Roo.Resizable(this.list,  {
23172                pinned:true, handles:'se'
23173             });
23174             this.resizer.on('resize', function(r, w, h){
23175                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23176                 this.listWidth = w;
23177                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23178                 this.restrictHeight();
23179             }, this);
23180             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23181         }
23182         if(!this.editable){
23183             this.editable = true;
23184             this.setEditable(false);
23185         }  
23186         
23187         
23188         if (typeof(this.events.add.listeners) != 'undefined') {
23189             
23190             this.addicon = this.wrap.createChild(
23191                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23192        
23193             this.addicon.on('click', function(e) {
23194                 this.fireEvent('add', this);
23195             }, this);
23196         }
23197         if (typeof(this.events.edit.listeners) != 'undefined') {
23198             
23199             this.editicon = this.wrap.createChild(
23200                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23201             if (this.addicon) {
23202                 this.editicon.setStyle('margin-left', '40px');
23203             }
23204             this.editicon.on('click', function(e) {
23205                 
23206                 // we fire even  if inothing is selected..
23207                 this.fireEvent('edit', this, this.lastData );
23208                 
23209             }, this);
23210         }
23211         
23212         
23213         
23214     },
23215
23216     // private
23217     initEvents : function(){
23218         Roo.form.ComboBox.superclass.initEvents.call(this);
23219
23220         this.keyNav = new Roo.KeyNav(this.el, {
23221             "up" : function(e){
23222                 this.inKeyMode = true;
23223                 this.selectPrev();
23224             },
23225
23226             "down" : function(e){
23227                 if(!this.isExpanded()){
23228                     this.onTriggerClick();
23229                 }else{
23230                     this.inKeyMode = true;
23231                     this.selectNext();
23232                 }
23233             },
23234
23235             "enter" : function(e){
23236                 this.onViewClick();
23237                 //return true;
23238             },
23239
23240             "esc" : function(e){
23241                 this.collapse();
23242             },
23243
23244             "tab" : function(e){
23245                 this.onViewClick(false);
23246                 this.fireEvent("specialkey", this, e);
23247                 return true;
23248             },
23249
23250             scope : this,
23251
23252             doRelay : function(foo, bar, hname){
23253                 if(hname == 'down' || this.scope.isExpanded()){
23254                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23255                 }
23256                 return true;
23257             },
23258
23259             forceKeyDown: true
23260         });
23261         this.queryDelay = Math.max(this.queryDelay || 10,
23262                 this.mode == 'local' ? 10 : 250);
23263         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23264         if(this.typeAhead){
23265             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23266         }
23267         if(this.editable !== false){
23268             this.el.on("keyup", this.onKeyUp, this);
23269         }
23270         if(this.forceSelection){
23271             this.on('blur', this.doForce, this);
23272         }
23273     },
23274
23275     onDestroy : function(){
23276         if(this.view){
23277             this.view.setStore(null);
23278             this.view.el.removeAllListeners();
23279             this.view.el.remove();
23280             this.view.purgeListeners();
23281         }
23282         if(this.list){
23283             this.list.destroy();
23284         }
23285         if(this.store){
23286             this.store.un('beforeload', this.onBeforeLoad, this);
23287             this.store.un('load', this.onLoad, this);
23288             this.store.un('loadexception', this.onLoadException, this);
23289         }
23290         Roo.form.ComboBox.superclass.onDestroy.call(this);
23291     },
23292
23293     // private
23294     fireKey : function(e){
23295         if(e.isNavKeyPress() && !this.list.isVisible()){
23296             this.fireEvent("specialkey", this, e);
23297         }
23298     },
23299
23300     // private
23301     onResize: function(w, h){
23302         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23303         
23304         if(typeof w != 'number'){
23305             // we do not handle it!?!?
23306             return;
23307         }
23308         var tw = this.trigger.getWidth();
23309         tw += this.addicon ? this.addicon.getWidth() : 0;
23310         tw += this.editicon ? this.editicon.getWidth() : 0;
23311         var x = w - tw;
23312         this.el.setWidth( this.adjustWidth('input', x));
23313             
23314         this.trigger.setStyle('left', x+'px');
23315         
23316         if(this.list && this.listWidth === undefined){
23317             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23318             this.list.setWidth(lw);
23319             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23320         }
23321         
23322     
23323         
23324     },
23325
23326     /**
23327      * Allow or prevent the user from directly editing the field text.  If false is passed,
23328      * the user will only be able to select from the items defined in the dropdown list.  This method
23329      * is the runtime equivalent of setting the 'editable' config option at config time.
23330      * @param {Boolean} value True to allow the user to directly edit the field text
23331      */
23332     setEditable : function(value){
23333         if(value == this.editable){
23334             return;
23335         }
23336         this.editable = value;
23337         if(!value){
23338             this.el.dom.setAttribute('readOnly', true);
23339             this.el.on('mousedown', this.onTriggerClick,  this);
23340             this.el.addClass('x-combo-noedit');
23341         }else{
23342             this.el.dom.setAttribute('readOnly', false);
23343             this.el.un('mousedown', this.onTriggerClick,  this);
23344             this.el.removeClass('x-combo-noedit');
23345         }
23346     },
23347
23348     // private
23349     onBeforeLoad : function(){
23350         if(!this.hasFocus){
23351             return;
23352         }
23353         this.innerList.update(this.loadingText ?
23354                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23355         this.restrictHeight();
23356         this.selectedIndex = -1;
23357     },
23358
23359     // private
23360     onLoad : function(){
23361         if(!this.hasFocus){
23362             return;
23363         }
23364         if(this.store.getCount() > 0){
23365             this.expand();
23366             this.restrictHeight();
23367             if(this.lastQuery == this.allQuery){
23368                 if(this.editable){
23369                     this.el.dom.select();
23370                 }
23371                 if(!this.selectByValue(this.value, true)){
23372                     this.select(0, true);
23373                 }
23374             }else{
23375                 this.selectNext();
23376                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23377                     this.taTask.delay(this.typeAheadDelay);
23378                 }
23379             }
23380         }else{
23381             this.onEmptyResults();
23382         }
23383         //this.el.focus();
23384     },
23385     // private
23386     onLoadException : function()
23387     {
23388         this.collapse();
23389         Roo.log(this.store.reader.jsonData);
23390         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23391             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23392         }
23393         
23394         
23395     },
23396     // private
23397     onTypeAhead : function(){
23398         if(this.store.getCount() > 0){
23399             var r = this.store.getAt(0);
23400             var newValue = r.data[this.displayField];
23401             var len = newValue.length;
23402             var selStart = this.getRawValue().length;
23403             if(selStart != len){
23404                 this.setRawValue(newValue);
23405                 this.selectText(selStart, newValue.length);
23406             }
23407         }
23408     },
23409
23410     // private
23411     onSelect : function(record, index){
23412         if(this.fireEvent('beforeselect', this, record, index) !== false){
23413             this.setFromData(index > -1 ? record.data : false);
23414             this.collapse();
23415             this.fireEvent('select', this, record, index);
23416         }
23417     },
23418
23419     /**
23420      * Returns the currently selected field value or empty string if no value is set.
23421      * @return {String} value The selected value
23422      */
23423     getValue : function(){
23424         if(this.valueField){
23425             return typeof this.value != 'undefined' ? this.value : '';
23426         }
23427         return Roo.form.ComboBox.superclass.getValue.call(this);
23428     },
23429
23430     /**
23431      * Clears any text/value currently set in the field
23432      */
23433     clearValue : function(){
23434         if(this.hiddenField){
23435             this.hiddenField.value = '';
23436         }
23437         this.value = '';
23438         this.setRawValue('');
23439         this.lastSelectionText = '';
23440         
23441     },
23442
23443     /**
23444      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23445      * will be displayed in the field.  If the value does not match the data value of an existing item,
23446      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23447      * Otherwise the field will be blank (although the value will still be set).
23448      * @param {String} value The value to match
23449      */
23450     setValue : function(v){
23451         var text = v;
23452         if(this.valueField){
23453             var r = this.findRecord(this.valueField, v);
23454             if(r){
23455                 text = r.data[this.displayField];
23456             }else if(this.valueNotFoundText !== undefined){
23457                 text = this.valueNotFoundText;
23458             }
23459         }
23460         this.lastSelectionText = text;
23461         if(this.hiddenField){
23462             this.hiddenField.value = v;
23463         }
23464         Roo.form.ComboBox.superclass.setValue.call(this, text);
23465         this.value = v;
23466     },
23467     /**
23468      * @property {Object} the last set data for the element
23469      */
23470     
23471     lastData : false,
23472     /**
23473      * Sets the value of the field based on a object which is related to the record format for the store.
23474      * @param {Object} value the value to set as. or false on reset?
23475      */
23476     setFromData : function(o){
23477         var dv = ''; // display value
23478         var vv = ''; // value value..
23479         this.lastData = o;
23480         if (this.displayField) {
23481             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23482         } else {
23483             // this is an error condition!!!
23484             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23485         }
23486         
23487         if(this.valueField){
23488             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23489         }
23490         if(this.hiddenField){
23491             this.hiddenField.value = vv;
23492             
23493             this.lastSelectionText = dv;
23494             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23495             this.value = vv;
23496             return;
23497         }
23498         // no hidden field.. - we store the value in 'value', but still display
23499         // display field!!!!
23500         this.lastSelectionText = dv;
23501         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23502         this.value = vv;
23503         
23504         
23505     },
23506     // private
23507     reset : function(){
23508         // overridden so that last data is reset..
23509         this.setValue(this.resetValue);
23510         this.clearInvalid();
23511         this.lastData = false;
23512         if (this.view) {
23513             this.view.clearSelections();
23514         }
23515     },
23516     // private
23517     findRecord : function(prop, value){
23518         var record;
23519         if(this.store.getCount() > 0){
23520             this.store.each(function(r){
23521                 if(r.data[prop] == value){
23522                     record = r;
23523                     return false;
23524                 }
23525                 return true;
23526             });
23527         }
23528         return record;
23529     },
23530     
23531     getName: function()
23532     {
23533         // returns hidden if it's set..
23534         if (!this.rendered) {return ''};
23535         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23536         
23537     },
23538     // private
23539     onViewMove : function(e, t){
23540         this.inKeyMode = false;
23541     },
23542
23543     // private
23544     onViewOver : function(e, t){
23545         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23546             return;
23547         }
23548         var item = this.view.findItemFromChild(t);
23549         if(item){
23550             var index = this.view.indexOf(item);
23551             this.select(index, false);
23552         }
23553     },
23554
23555     // private
23556     onViewClick : function(doFocus)
23557     {
23558         var index = this.view.getSelectedIndexes()[0];
23559         var r = this.store.getAt(index);
23560         if(r){
23561             this.onSelect(r, index);
23562         }
23563         if(doFocus !== false && !this.blockFocus){
23564             this.el.focus();
23565         }
23566     },
23567
23568     // private
23569     restrictHeight : function(){
23570         this.innerList.dom.style.height = '';
23571         var inner = this.innerList.dom;
23572         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23573         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23574         this.list.beginUpdate();
23575         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23576         this.list.alignTo(this.el, this.listAlign);
23577         this.list.endUpdate();
23578     },
23579
23580     // private
23581     onEmptyResults : function(){
23582         this.collapse();
23583     },
23584
23585     /**
23586      * Returns true if the dropdown list is expanded, else false.
23587      */
23588     isExpanded : function(){
23589         return this.list.isVisible();
23590     },
23591
23592     /**
23593      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23594      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23595      * @param {String} value The data value of the item to select
23596      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23597      * selected item if it is not currently in view (defaults to true)
23598      * @return {Boolean} True if the value matched an item in the list, else false
23599      */
23600     selectByValue : function(v, scrollIntoView){
23601         if(v !== undefined && v !== null){
23602             var r = this.findRecord(this.valueField || this.displayField, v);
23603             if(r){
23604                 this.select(this.store.indexOf(r), scrollIntoView);
23605                 return true;
23606             }
23607         }
23608         return false;
23609     },
23610
23611     /**
23612      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23613      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23614      * @param {Number} index The zero-based index of the list item to select
23615      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23616      * selected item if it is not currently in view (defaults to true)
23617      */
23618     select : function(index, scrollIntoView){
23619         this.selectedIndex = index;
23620         this.view.select(index);
23621         if(scrollIntoView !== false){
23622             var el = this.view.getNode(index);
23623             if(el){
23624                 this.innerList.scrollChildIntoView(el, false);
23625             }
23626         }
23627     },
23628
23629     // private
23630     selectNext : function(){
23631         var ct = this.store.getCount();
23632         if(ct > 0){
23633             if(this.selectedIndex == -1){
23634                 this.select(0);
23635             }else if(this.selectedIndex < ct-1){
23636                 this.select(this.selectedIndex+1);
23637             }
23638         }
23639     },
23640
23641     // private
23642     selectPrev : function(){
23643         var ct = this.store.getCount();
23644         if(ct > 0){
23645             if(this.selectedIndex == -1){
23646                 this.select(0);
23647             }else if(this.selectedIndex != 0){
23648                 this.select(this.selectedIndex-1);
23649             }
23650         }
23651     },
23652
23653     // private
23654     onKeyUp : function(e){
23655         if(this.editable !== false && !e.isSpecialKey()){
23656             this.lastKey = e.getKey();
23657             this.dqTask.delay(this.queryDelay);
23658         }
23659     },
23660
23661     // private
23662     validateBlur : function(){
23663         return !this.list || !this.list.isVisible();   
23664     },
23665
23666     // private
23667     initQuery : function(){
23668         this.doQuery(this.getRawValue());
23669     },
23670
23671     // private
23672     doForce : function(){
23673         if(this.el.dom.value.length > 0){
23674             this.el.dom.value =
23675                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23676              
23677         }
23678     },
23679
23680     /**
23681      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23682      * query allowing the query action to be canceled if needed.
23683      * @param {String} query The SQL query to execute
23684      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23685      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23686      * saved in the current store (defaults to false)
23687      */
23688     doQuery : function(q, forceAll){
23689         if(q === undefined || q === null){
23690             q = '';
23691         }
23692         var qe = {
23693             query: q,
23694             forceAll: forceAll,
23695             combo: this,
23696             cancel:false
23697         };
23698         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23699             return false;
23700         }
23701         q = qe.query;
23702         forceAll = qe.forceAll;
23703         if(forceAll === true || (q.length >= this.minChars)){
23704             if(this.lastQuery != q || this.alwaysQuery){
23705                 this.lastQuery = q;
23706                 if(this.mode == 'local'){
23707                     this.selectedIndex = -1;
23708                     if(forceAll){
23709                         this.store.clearFilter();
23710                     }else{
23711                         this.store.filter(this.displayField, q);
23712                     }
23713                     this.onLoad();
23714                 }else{
23715                     this.store.baseParams[this.queryParam] = q;
23716                     this.store.load({
23717                         params: this.getParams(q)
23718                     });
23719                     this.expand();
23720                 }
23721             }else{
23722                 this.selectedIndex = -1;
23723                 this.onLoad();   
23724             }
23725         }
23726     },
23727
23728     // private
23729     getParams : function(q){
23730         var p = {};
23731         //p[this.queryParam] = q;
23732         if(this.pageSize){
23733             p.start = 0;
23734             p.limit = this.pageSize;
23735         }
23736         return p;
23737     },
23738
23739     /**
23740      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23741      */
23742     collapse : function(){
23743         if(!this.isExpanded()){
23744             return;
23745         }
23746         this.list.hide();
23747         Roo.get(document).un('mousedown', this.collapseIf, this);
23748         Roo.get(document).un('mousewheel', this.collapseIf, this);
23749         if (!this.editable) {
23750             Roo.get(document).un('keydown', this.listKeyPress, this);
23751         }
23752         this.fireEvent('collapse', this);
23753     },
23754
23755     // private
23756     collapseIf : function(e){
23757         if(!e.within(this.wrap) && !e.within(this.list)){
23758             this.collapse();
23759         }
23760     },
23761
23762     /**
23763      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23764      */
23765     expand : function(){
23766         if(this.isExpanded() || !this.hasFocus){
23767             return;
23768         }
23769         this.list.alignTo(this.el, this.listAlign);
23770         this.list.show();
23771         Roo.get(document).on('mousedown', this.collapseIf, this);
23772         Roo.get(document).on('mousewheel', this.collapseIf, this);
23773         if (!this.editable) {
23774             Roo.get(document).on('keydown', this.listKeyPress, this);
23775         }
23776         
23777         this.fireEvent('expand', this);
23778     },
23779
23780     // private
23781     // Implements the default empty TriggerField.onTriggerClick function
23782     onTriggerClick : function(){
23783         if(this.disabled){
23784             return;
23785         }
23786         if(this.isExpanded()){
23787             this.collapse();
23788             if (!this.blockFocus) {
23789                 this.el.focus();
23790             }
23791             
23792         }else {
23793             this.hasFocus = true;
23794             if(this.triggerAction == 'all') {
23795                 this.doQuery(this.allQuery, true);
23796             } else {
23797                 this.doQuery(this.getRawValue());
23798             }
23799             if (!this.blockFocus) {
23800                 this.el.focus();
23801             }
23802         }
23803     },
23804     listKeyPress : function(e)
23805     {
23806         //Roo.log('listkeypress');
23807         // scroll to first matching element based on key pres..
23808         if (e.isSpecialKey()) {
23809             return false;
23810         }
23811         var k = String.fromCharCode(e.getKey()).toUpperCase();
23812         //Roo.log(k);
23813         var match  = false;
23814         var csel = this.view.getSelectedNodes();
23815         var cselitem = false;
23816         if (csel.length) {
23817             var ix = this.view.indexOf(csel[0]);
23818             cselitem  = this.store.getAt(ix);
23819             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23820                 cselitem = false;
23821             }
23822             
23823         }
23824         
23825         this.store.each(function(v) { 
23826             if (cselitem) {
23827                 // start at existing selection.
23828                 if (cselitem.id == v.id) {
23829                     cselitem = false;
23830                 }
23831                 return;
23832             }
23833                 
23834             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23835                 match = this.store.indexOf(v);
23836                 return false;
23837             }
23838         }, this);
23839         
23840         if (match === false) {
23841             return true; // no more action?
23842         }
23843         // scroll to?
23844         this.view.select(match);
23845         var sn = Roo.get(this.view.getSelectedNodes()[0])
23846         sn.scrollIntoView(sn.dom.parentNode, false);
23847     }
23848
23849     /** 
23850     * @cfg {Boolean} grow 
23851     * @hide 
23852     */
23853     /** 
23854     * @cfg {Number} growMin 
23855     * @hide 
23856     */
23857     /** 
23858     * @cfg {Number} growMax 
23859     * @hide 
23860     */
23861     /**
23862      * @hide
23863      * @method autoSize
23864      */
23865 });/*
23866  * Copyright(c) 2010-2012, Roo J Solutions Limited
23867  *
23868  * Licence LGPL
23869  *
23870  */
23871
23872 /**
23873  * @class Roo.form.ComboBoxArray
23874  * @extends Roo.form.TextField
23875  * A facebook style adder... for lists of email / people / countries  etc...
23876  * pick multiple items from a combo box, and shows each one.
23877  *
23878  *  Fred [x]  Brian [x]  [Pick another |v]
23879  *
23880  *
23881  *  For this to work: it needs various extra information
23882  *    - normal combo problay has
23883  *      name, hiddenName
23884  *    + displayField, valueField
23885  *
23886  *    For our purpose...
23887  *
23888  *
23889  *   If we change from 'extends' to wrapping...
23890  *   
23891  *  
23892  *
23893  
23894  
23895  * @constructor
23896  * Create a new ComboBoxArray.
23897  * @param {Object} config Configuration options
23898  */
23899  
23900
23901 Roo.form.ComboBoxArray = function(config)
23902 {
23903     this.addEvents({
23904         /**
23905          * @event beforeremove
23906          * Fires before remove the value from the list
23907              * @param {Roo.form.ComboBoxArray} _self This combo box array
23908              * @param {Roo.form.ComboBoxArray.Item} item removed item
23909              */
23910         'beforeremove' : true,
23911         /**
23912          * @event remove
23913          * Fires when remove the value from the list
23914              * @param {Roo.form.ComboBoxArray} _self This combo box array
23915              * @param {Roo.form.ComboBoxArray.Item} item removed item
23916              */
23917         'remove' : true
23918         
23919         
23920     });
23921     
23922     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23923     
23924     this.items = new Roo.util.MixedCollection(false);
23925     
23926     // construct the child combo...
23927     
23928     
23929     
23930     
23931    
23932     
23933 }
23934
23935  
23936 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23937
23938     /**
23939      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23940      */
23941     
23942     lastData : false,
23943     
23944     // behavies liek a hiddne field
23945     inputType:      'hidden',
23946     /**
23947      * @cfg {Number} width The width of the box that displays the selected element
23948      */ 
23949     width:          300,
23950
23951     
23952     
23953     /**
23954      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23955      */
23956     name : false,
23957     /**
23958      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23959      */
23960     hiddenName : false,
23961     
23962     
23963     // private the array of items that are displayed..
23964     items  : false,
23965     // private - the hidden field el.
23966     hiddenEl : false,
23967     // private - the filed el..
23968     el : false,
23969     
23970     //validateValue : function() { return true; }, // all values are ok!
23971     //onAddClick: function() { },
23972     
23973     onRender : function(ct, position) 
23974     {
23975         
23976         // create the standard hidden element
23977         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23978         
23979         
23980         // give fake names to child combo;
23981         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23982         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23983         
23984         this.combo = Roo.factory(this.combo, Roo.form);
23985         this.combo.onRender(ct, position);
23986         if (typeof(this.combo.width) != 'undefined') {
23987             this.combo.onResize(this.combo.width,0);
23988         }
23989         
23990         this.combo.initEvents();
23991         
23992         // assigned so form know we need to do this..
23993         this.store          = this.combo.store;
23994         this.valueField     = this.combo.valueField;
23995         this.displayField   = this.combo.displayField ;
23996         
23997         
23998         this.combo.wrap.addClass('x-cbarray-grp');
23999         
24000         var cbwrap = this.combo.wrap.createChild(
24001             {tag: 'div', cls: 'x-cbarray-cb'},
24002             this.combo.el.dom
24003         );
24004         
24005              
24006         this.hiddenEl = this.combo.wrap.createChild({
24007             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24008         });
24009         this.el = this.combo.wrap.createChild({
24010             tag: 'input',  type:'hidden' , name: this.name, value : ''
24011         });
24012          //   this.el.dom.removeAttribute("name");
24013         
24014         
24015         this.outerWrap = this.combo.wrap;
24016         this.wrap = cbwrap;
24017         
24018         this.outerWrap.setWidth(this.width);
24019         this.outerWrap.dom.removeChild(this.el.dom);
24020         
24021         this.wrap.dom.appendChild(this.el.dom);
24022         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24023         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24024         
24025         this.combo.trigger.setStyle('position','relative');
24026         this.combo.trigger.setStyle('left', '0px');
24027         this.combo.trigger.setStyle('top', '2px');
24028         
24029         this.combo.el.setStyle('vertical-align', 'text-bottom');
24030         
24031         //this.trigger.setStyle('vertical-align', 'top');
24032         
24033         // this should use the code from combo really... on('add' ....)
24034         if (this.adder) {
24035             
24036         
24037             this.adder = this.outerWrap.createChild(
24038                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24039             var _t = this;
24040             this.adder.on('click', function(e) {
24041                 _t.fireEvent('adderclick', this, e);
24042             }, _t);
24043         }
24044         //var _t = this;
24045         //this.adder.on('click', this.onAddClick, _t);
24046         
24047         
24048         this.combo.on('select', function(cb, rec, ix) {
24049             this.addItem(rec.data);
24050             
24051             cb.setValue('');
24052             cb.el.dom.value = '';
24053             //cb.lastData = rec.data;
24054             // add to list
24055             
24056         }, this);
24057         
24058         
24059     },
24060     
24061     
24062     getName: function()
24063     {
24064         // returns hidden if it's set..
24065         if (!this.rendered) {return ''};
24066         return  this.hiddenName ? this.hiddenName : this.name;
24067         
24068     },
24069     
24070     
24071     onResize: function(w, h){
24072         
24073         return;
24074         // not sure if this is needed..
24075         //this.combo.onResize(w,h);
24076         
24077         if(typeof w != 'number'){
24078             // we do not handle it!?!?
24079             return;
24080         }
24081         var tw = this.combo.trigger.getWidth();
24082         tw += this.addicon ? this.addicon.getWidth() : 0;
24083         tw += this.editicon ? this.editicon.getWidth() : 0;
24084         var x = w - tw;
24085         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24086             
24087         this.combo.trigger.setStyle('left', '0px');
24088         
24089         if(this.list && this.listWidth === undefined){
24090             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24091             this.list.setWidth(lw);
24092             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24093         }
24094         
24095     
24096         
24097     },
24098     
24099     addItem: function(rec)
24100     {
24101         var valueField = this.combo.valueField;
24102         var displayField = this.combo.displayField;
24103         if (this.items.indexOfKey(rec[valueField]) > -1) {
24104             //console.log("GOT " + rec.data.id);
24105             return;
24106         }
24107         
24108         var x = new Roo.form.ComboBoxArray.Item({
24109             //id : rec[this.idField],
24110             data : rec,
24111             displayField : displayField ,
24112             tipField : displayField ,
24113             cb : this
24114         });
24115         // use the 
24116         this.items.add(rec[valueField],x);
24117         // add it before the element..
24118         this.updateHiddenEl();
24119         x.render(this.outerWrap, this.wrap.dom);
24120         // add the image handler..
24121     },
24122     
24123     updateHiddenEl : function()
24124     {
24125         this.validate();
24126         if (!this.hiddenEl) {
24127             return;
24128         }
24129         var ar = [];
24130         var idField = this.combo.valueField;
24131         
24132         this.items.each(function(f) {
24133             ar.push(f.data[idField]);
24134            
24135         });
24136         this.hiddenEl.dom.value = ar.join(',');
24137         this.validate();
24138     },
24139     
24140     reset : function()
24141     {
24142         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24143         this.items.each(function(f) {
24144            f.remove(); 
24145         });
24146         this.el.dom.value = '';
24147         if (this.hiddenEl) {
24148             this.hiddenEl.dom.value = '';
24149         }
24150         
24151     },
24152     getValue: function()
24153     {
24154         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24155     },
24156     setValue: function(v) // not a valid action - must use addItems..
24157     {
24158          
24159         this.reset();
24160         
24161         
24162         
24163         if (this.store.isLocal && (typeof(v) == 'string')) {
24164             // then we can use the store to find the values..
24165             // comma seperated at present.. this needs to allow JSON based encoding..
24166             this.hiddenEl.value  = v;
24167             var v_ar = [];
24168             Roo.each(v.split(','), function(k) {
24169                 Roo.log("CHECK " + this.valueField + ',' + k);
24170                 var li = this.store.query(this.valueField, k);
24171                 if (!li.length) {
24172                     return;
24173                 }
24174                 var add = {};
24175                 add[this.valueField] = k;
24176                 add[this.displayField] = li.item(0).data[this.displayField];
24177                 
24178                 this.addItem(add);
24179             }, this) 
24180              
24181         }
24182         if (typeof(v) == 'object' ) {
24183             // then let's assume it's an array of objects..
24184             Roo.each(v, function(l) {
24185                 this.addItem(l);
24186             }, this);
24187              
24188         }
24189         
24190         
24191     },
24192     setFromData: function(v)
24193     {
24194         // this recieves an object, if setValues is called.
24195         this.reset();
24196         this.el.dom.value = v[this.displayField];
24197         this.hiddenEl.dom.value = v[this.valueField];
24198         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24199             return;
24200         }
24201         var kv = v[this.valueField];
24202         var dv = v[this.displayField];
24203         kv = typeof(kv) != 'string' ? '' : kv;
24204         dv = typeof(dv) != 'string' ? '' : dv;
24205         
24206         
24207         var keys = kv.split(',');
24208         var display = dv.split(',');
24209         for (var i = 0 ; i < keys.length; i++) {
24210             
24211             add = {};
24212             add[this.valueField] = keys[i];
24213             add[this.displayField] = display[i];
24214             this.addItem(add);
24215         }
24216       
24217         
24218     },
24219     
24220     /**
24221      * Validates the combox array value
24222      * @return {Boolean} True if the value is valid, else false
24223      */
24224     validate : function(){
24225         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24226             this.clearInvalid();
24227             return true;
24228         }
24229         return false;
24230     },
24231     
24232     validateValue : function(value){
24233         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24234         
24235     },
24236     
24237     /*@
24238      * overide
24239      * 
24240      */
24241     isDirty : function() {
24242         if(this.disabled) {
24243             return false;
24244         }
24245         
24246         try {
24247             var d = Roo.decode(String(this.originalValue));
24248         } catch (e) {
24249             return String(this.getValue()) !== String(this.originalValue);
24250         }
24251         
24252         var originalValue = [];
24253         
24254         for (var i = 0; i < d.length; i++){
24255             originalValue.push(d[i][this.valueField]);
24256         }
24257         
24258         return String(this.getValue()) !== String(originalValue.join(','));
24259         
24260     }
24261     
24262 });
24263
24264
24265
24266 /**
24267  * @class Roo.form.ComboBoxArray.Item
24268  * @extends Roo.BoxComponent
24269  * A selected item in the list
24270  *  Fred [x]  Brian [x]  [Pick another |v]
24271  * 
24272  * @constructor
24273  * Create a new item.
24274  * @param {Object} config Configuration options
24275  */
24276  
24277 Roo.form.ComboBoxArray.Item = function(config) {
24278     config.id = Roo.id();
24279     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24280 }
24281
24282 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24283     data : {},
24284     cb: false,
24285     displayField : false,
24286     tipField : false,
24287     
24288     
24289     defaultAutoCreate : {
24290         tag: 'div',
24291         cls: 'x-cbarray-item',
24292         cn : [ 
24293             { tag: 'div' },
24294             {
24295                 tag: 'img',
24296                 width:16,
24297                 height : 16,
24298                 src : Roo.BLANK_IMAGE_URL ,
24299                 align: 'center'
24300             }
24301         ]
24302         
24303     },
24304     
24305  
24306     onRender : function(ct, position)
24307     {
24308         Roo.form.Field.superclass.onRender.call(this, ct, position);
24309         
24310         if(!this.el){
24311             var cfg = this.getAutoCreate();
24312             this.el = ct.createChild(cfg, position);
24313         }
24314         
24315         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24316         
24317         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24318             this.cb.renderer(this.data) :
24319             String.format('{0}',this.data[this.displayField]);
24320         
24321             
24322         this.el.child('div').dom.setAttribute('qtip',
24323                         String.format('{0}',this.data[this.tipField])
24324         );
24325         
24326         this.el.child('img').on('click', this.remove, this);
24327         
24328     },
24329    
24330     remove : function()
24331     {
24332         if(this.cb.disabled){
24333             return;
24334         }
24335         
24336         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24337             this.cb.items.remove(this);
24338             this.el.child('img').un('click', this.remove, this);
24339             this.el.remove();
24340             this.cb.updateHiddenEl();
24341
24342             this.cb.fireEvent('remove', this.cb, this);
24343         }
24344         
24345     }
24346 });/*
24347  * Based on:
24348  * Ext JS Library 1.1.1
24349  * Copyright(c) 2006-2007, Ext JS, LLC.
24350  *
24351  * Originally Released Under LGPL - original licence link has changed is not relivant.
24352  *
24353  * Fork - LGPL
24354  * <script type="text/javascript">
24355  */
24356 /**
24357  * @class Roo.form.Checkbox
24358  * @extends Roo.form.Field
24359  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24360  * @constructor
24361  * Creates a new Checkbox
24362  * @param {Object} config Configuration options
24363  */
24364 Roo.form.Checkbox = function(config){
24365     Roo.form.Checkbox.superclass.constructor.call(this, config);
24366     this.addEvents({
24367         /**
24368          * @event check
24369          * Fires when the checkbox is checked or unchecked.
24370              * @param {Roo.form.Checkbox} this This checkbox
24371              * @param {Boolean} checked The new checked value
24372              */
24373         check : true
24374     });
24375 };
24376
24377 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24378     /**
24379      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24380      */
24381     focusClass : undefined,
24382     /**
24383      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24384      */
24385     fieldClass: "x-form-field",
24386     /**
24387      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24388      */
24389     checked: false,
24390     /**
24391      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24392      * {tag: "input", type: "checkbox", autocomplete: "off"})
24393      */
24394     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24395     /**
24396      * @cfg {String} boxLabel The text that appears beside the checkbox
24397      */
24398     boxLabel : "",
24399     /**
24400      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24401      */  
24402     inputValue : '1',
24403     /**
24404      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24405      */
24406      valueOff: '0', // value when not checked..
24407
24408     actionMode : 'viewEl', 
24409     //
24410     // private
24411     itemCls : 'x-menu-check-item x-form-item',
24412     groupClass : 'x-menu-group-item',
24413     inputType : 'hidden',
24414     
24415     
24416     inSetChecked: false, // check that we are not calling self...
24417     
24418     inputElement: false, // real input element?
24419     basedOn: false, // ????
24420     
24421     isFormField: true, // not sure where this is needed!!!!
24422
24423     onResize : function(){
24424         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24425         if(!this.boxLabel){
24426             this.el.alignTo(this.wrap, 'c-c');
24427         }
24428     },
24429
24430     initEvents : function(){
24431         Roo.form.Checkbox.superclass.initEvents.call(this);
24432         this.el.on("click", this.onClick,  this);
24433         this.el.on("change", this.onClick,  this);
24434     },
24435
24436
24437     getResizeEl : function(){
24438         return this.wrap;
24439     },
24440
24441     getPositionEl : function(){
24442         return this.wrap;
24443     },
24444
24445     // private
24446     onRender : function(ct, position){
24447         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24448         /*
24449         if(this.inputValue !== undefined){
24450             this.el.dom.value = this.inputValue;
24451         }
24452         */
24453         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24454         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24455         var viewEl = this.wrap.createChild({ 
24456             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24457         this.viewEl = viewEl;   
24458         this.wrap.on('click', this.onClick,  this); 
24459         
24460         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24461         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24462         
24463         
24464         
24465         if(this.boxLabel){
24466             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24467         //    viewEl.on('click', this.onClick,  this); 
24468         }
24469         //if(this.checked){
24470             this.setChecked(this.checked);
24471         //}else{
24472             //this.checked = this.el.dom;
24473         //}
24474
24475     },
24476
24477     // private
24478     initValue : Roo.emptyFn,
24479
24480     /**
24481      * Returns the checked state of the checkbox.
24482      * @return {Boolean} True if checked, else false
24483      */
24484     getValue : function(){
24485         if(this.el){
24486             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24487         }
24488         return this.valueOff;
24489         
24490     },
24491
24492         // private
24493     onClick : function(){ 
24494         if (this.disabled) {
24495             return;
24496         }
24497         this.setChecked(!this.checked);
24498
24499         //if(this.el.dom.checked != this.checked){
24500         //    this.setValue(this.el.dom.checked);
24501        // }
24502     },
24503
24504     /**
24505      * Sets the checked state of the checkbox.
24506      * On is always based on a string comparison between inputValue and the param.
24507      * @param {Boolean/String} value - the value to set 
24508      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24509      */
24510     setValue : function(v,suppressEvent){
24511         
24512         
24513         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24514         //if(this.el && this.el.dom){
24515         //    this.el.dom.checked = this.checked;
24516         //    this.el.dom.defaultChecked = this.checked;
24517         //}
24518         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24519         //this.fireEvent("check", this, this.checked);
24520     },
24521     // private..
24522     setChecked : function(state,suppressEvent)
24523     {
24524         if (this.inSetChecked) {
24525             this.checked = state;
24526             return;
24527         }
24528         
24529     
24530         if(this.wrap){
24531             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24532         }
24533         this.checked = state;
24534         if(suppressEvent !== true){
24535             this.fireEvent('check', this, state);
24536         }
24537         this.inSetChecked = true;
24538         this.el.dom.value = state ? this.inputValue : this.valueOff;
24539         this.inSetChecked = false;
24540         
24541     },
24542     // handle setting of hidden value by some other method!!?!?
24543     setFromHidden: function()
24544     {
24545         if(!this.el){
24546             return;
24547         }
24548         //console.log("SET FROM HIDDEN");
24549         //alert('setFrom hidden');
24550         this.setValue(this.el.dom.value);
24551     },
24552     
24553     onDestroy : function()
24554     {
24555         if(this.viewEl){
24556             Roo.get(this.viewEl).remove();
24557         }
24558          
24559         Roo.form.Checkbox.superclass.onDestroy.call(this);
24560     }
24561
24562 });/*
24563  * Based on:
24564  * Ext JS Library 1.1.1
24565  * Copyright(c) 2006-2007, Ext JS, LLC.
24566  *
24567  * Originally Released Under LGPL - original licence link has changed is not relivant.
24568  *
24569  * Fork - LGPL
24570  * <script type="text/javascript">
24571  */
24572  
24573 /**
24574  * @class Roo.form.Radio
24575  * @extends Roo.form.Checkbox
24576  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24577  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24578  * @constructor
24579  * Creates a new Radio
24580  * @param {Object} config Configuration options
24581  */
24582 Roo.form.Radio = function(){
24583     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24584 };
24585 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24586     inputType: 'radio',
24587
24588     /**
24589      * If this radio is part of a group, it will return the selected value
24590      * @return {String}
24591      */
24592     getGroupValue : function(){
24593         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24594     },
24595     
24596     
24597     onRender : function(ct, position){
24598         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24599         
24600         if(this.inputValue !== undefined){
24601             this.el.dom.value = this.inputValue;
24602         }
24603          
24604         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24605         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24606         //var viewEl = this.wrap.createChild({ 
24607         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24608         //this.viewEl = viewEl;   
24609         //this.wrap.on('click', this.onClick,  this); 
24610         
24611         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24612         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24613         
24614         
24615         
24616         if(this.boxLabel){
24617             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24618         //    viewEl.on('click', this.onClick,  this); 
24619         }
24620          if(this.checked){
24621             this.el.dom.checked =   'checked' ;
24622         }
24623          
24624     } 
24625     
24626     
24627 });//<script type="text/javascript">
24628
24629 /*
24630  * Based  Ext JS Library 1.1.1
24631  * Copyright(c) 2006-2007, Ext JS, LLC.
24632  * LGPL
24633  *
24634  */
24635  
24636 /**
24637  * @class Roo.HtmlEditorCore
24638  * @extends Roo.Component
24639  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24640  *
24641  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24642  */
24643
24644 Roo.HtmlEditorCore = function(config){
24645     
24646     
24647     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24648     
24649     
24650     this.addEvents({
24651         /**
24652          * @event initialize
24653          * Fires when the editor is fully initialized (including the iframe)
24654          * @param {Roo.HtmlEditorCore} this
24655          */
24656         initialize: true,
24657         /**
24658          * @event activate
24659          * Fires when the editor is first receives the focus. Any insertion must wait
24660          * until after this event.
24661          * @param {Roo.HtmlEditorCore} this
24662          */
24663         activate: true,
24664          /**
24665          * @event beforesync
24666          * Fires before the textarea is updated with content from the editor iframe. Return false
24667          * to cancel the sync.
24668          * @param {Roo.HtmlEditorCore} this
24669          * @param {String} html
24670          */
24671         beforesync: true,
24672          /**
24673          * @event beforepush
24674          * Fires before the iframe editor is updated with content from the textarea. Return false
24675          * to cancel the push.
24676          * @param {Roo.HtmlEditorCore} this
24677          * @param {String} html
24678          */
24679         beforepush: true,
24680          /**
24681          * @event sync
24682          * Fires when the textarea is updated with content from the editor iframe.
24683          * @param {Roo.HtmlEditorCore} this
24684          * @param {String} html
24685          */
24686         sync: true,
24687          /**
24688          * @event push
24689          * Fires when the iframe editor is updated with content from the textarea.
24690          * @param {Roo.HtmlEditorCore} this
24691          * @param {String} html
24692          */
24693         push: true,
24694         
24695         /**
24696          * @event editorevent
24697          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24698          * @param {Roo.HtmlEditorCore} this
24699          */
24700         editorevent: true
24701     });
24702     
24703     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24704     
24705     // defaults : white / black...
24706     this.applyBlacklists();
24707     
24708     
24709     
24710 };
24711
24712
24713 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24714
24715
24716      /**
24717      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24718      */
24719     
24720     owner : false,
24721     
24722      /**
24723      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24724      *                        Roo.resizable.
24725      */
24726     resizable : false,
24727      /**
24728      * @cfg {Number} height (in pixels)
24729      */   
24730     height: 300,
24731    /**
24732      * @cfg {Number} width (in pixels)
24733      */   
24734     width: 500,
24735     
24736     /**
24737      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24738      * 
24739      */
24740     stylesheets: false,
24741     
24742     // id of frame..
24743     frameId: false,
24744     
24745     // private properties
24746     validationEvent : false,
24747     deferHeight: true,
24748     initialized : false,
24749     activated : false,
24750     sourceEditMode : false,
24751     onFocus : Roo.emptyFn,
24752     iframePad:3,
24753     hideMode:'offsets',
24754     
24755     clearUp: true,
24756     
24757     // blacklist + whitelisted elements..
24758     black: false,
24759     white: false,
24760      
24761     
24762
24763     /**
24764      * Protected method that will not generally be called directly. It
24765      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24766      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24767      */
24768     getDocMarkup : function(){
24769         // body styles..
24770         var st = '';
24771         Roo.log(this.stylesheets);
24772         
24773         // inherit styels from page...?? 
24774         if (this.stylesheets === false) {
24775             
24776             Roo.get(document.head).select('style').each(function(node) {
24777                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24778             });
24779             
24780             Roo.get(document.head).select('link').each(function(node) { 
24781                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24782             });
24783             
24784         } else if (!this.stylesheets.length) {
24785                 // simple..
24786                 st = '<style type="text/css">' +
24787                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24788                    '</style>';
24789         } else {
24790             Roo.each(this.stylesheets, function(s) {
24791                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24792             });
24793             
24794         }
24795         
24796         st +=  '<style type="text/css">' +
24797             'IMG { cursor: pointer } ' +
24798         '</style>';
24799
24800         
24801         return '<html><head>' + st  +
24802             //<style type="text/css">' +
24803             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24804             //'</style>' +
24805             ' </head><body class="roo-htmleditor-body"></body></html>';
24806     },
24807
24808     // private
24809     onRender : function(ct, position)
24810     {
24811         var _t = this;
24812         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24813         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24814         
24815         
24816         this.el.dom.style.border = '0 none';
24817         this.el.dom.setAttribute('tabIndex', -1);
24818         this.el.addClass('x-hidden hide');
24819         
24820         
24821         
24822         if(Roo.isIE){ // fix IE 1px bogus margin
24823             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24824         }
24825        
24826         
24827         this.frameId = Roo.id();
24828         
24829          
24830         
24831         var iframe = this.owner.wrap.createChild({
24832             tag: 'iframe',
24833             cls: 'form-control', // bootstrap..
24834             id: this.frameId,
24835             name: this.frameId,
24836             frameBorder : 'no',
24837             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24838         }, this.el
24839         );
24840         
24841         
24842         this.iframe = iframe.dom;
24843
24844          this.assignDocWin();
24845         
24846         this.doc.designMode = 'on';
24847        
24848         this.doc.open();
24849         this.doc.write(this.getDocMarkup());
24850         this.doc.close();
24851
24852         
24853         var task = { // must defer to wait for browser to be ready
24854             run : function(){
24855                 //console.log("run task?" + this.doc.readyState);
24856                 this.assignDocWin();
24857                 if(this.doc.body || this.doc.readyState == 'complete'){
24858                     try {
24859                         this.doc.designMode="on";
24860                     } catch (e) {
24861                         return;
24862                     }
24863                     Roo.TaskMgr.stop(task);
24864                     this.initEditor.defer(10, this);
24865                 }
24866             },
24867             interval : 10,
24868             duration: 10000,
24869             scope: this
24870         };
24871         Roo.TaskMgr.start(task);
24872
24873         
24874          
24875     },
24876
24877     // private
24878     onResize : function(w, h)
24879     {
24880          Roo.log('resize: ' +w + ',' + h );
24881         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24882         if(!this.iframe){
24883             return;
24884         }
24885         if(typeof w == 'number'){
24886             
24887             this.iframe.style.width = w + 'px';
24888         }
24889         if(typeof h == 'number'){
24890             
24891             this.iframe.style.height = h + 'px';
24892             if(this.doc){
24893                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24894             }
24895         }
24896         
24897     },
24898
24899     /**
24900      * Toggles the editor between standard and source edit mode.
24901      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24902      */
24903     toggleSourceEdit : function(sourceEditMode){
24904         
24905         this.sourceEditMode = sourceEditMode === true;
24906         
24907         if(this.sourceEditMode){
24908  
24909             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24910             
24911         }else{
24912             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24913             //this.iframe.className = '';
24914             this.deferFocus();
24915         }
24916         //this.setSize(this.owner.wrap.getSize());
24917         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24918     },
24919
24920     
24921   
24922
24923     /**
24924      * Protected method that will not generally be called directly. If you need/want
24925      * custom HTML cleanup, this is the method you should override.
24926      * @param {String} html The HTML to be cleaned
24927      * return {String} The cleaned HTML
24928      */
24929     cleanHtml : function(html){
24930         html = String(html);
24931         if(html.length > 5){
24932             if(Roo.isSafari){ // strip safari nonsense
24933                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24934             }
24935         }
24936         if(html == '&nbsp;'){
24937             html = '';
24938         }
24939         return html;
24940     },
24941
24942     /**
24943      * HTML Editor -> Textarea
24944      * Protected method that will not generally be called directly. Syncs the contents
24945      * of the editor iframe with the textarea.
24946      */
24947     syncValue : function(){
24948         if(this.initialized){
24949             var bd = (this.doc.body || this.doc.documentElement);
24950             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24951             var html = bd.innerHTML;
24952             if(Roo.isSafari){
24953                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24954                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24955                 if(m && m[1]){
24956                     html = '<div style="'+m[0]+'">' + html + '</div>';
24957                 }
24958             }
24959             html = this.cleanHtml(html);
24960             // fix up the special chars.. normaly like back quotes in word...
24961             // however we do not want to do this with chinese..
24962             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24963                 var cc = b.charCodeAt();
24964                 if (
24965                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24966                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24967                     (cc >= 0xf900 && cc < 0xfb00 )
24968                 ) {
24969                         return b;
24970                 }
24971                 return "&#"+cc+";" 
24972             });
24973             if(this.owner.fireEvent('beforesync', this, html) !== false){
24974                 this.el.dom.value = html;
24975                 this.owner.fireEvent('sync', this, html);
24976             }
24977         }
24978     },
24979
24980     /**
24981      * Protected method that will not generally be called directly. Pushes the value of the textarea
24982      * into the iframe editor.
24983      */
24984     pushValue : function(){
24985         if(this.initialized){
24986             var v = this.el.dom.value.trim();
24987             
24988 //            if(v.length < 1){
24989 //                v = '&#160;';
24990 //            }
24991             
24992             if(this.owner.fireEvent('beforepush', this, v) !== false){
24993                 var d = (this.doc.body || this.doc.documentElement);
24994                 d.innerHTML = v;
24995                 this.cleanUpPaste();
24996                 this.el.dom.value = d.innerHTML;
24997                 this.owner.fireEvent('push', this, v);
24998             }
24999         }
25000     },
25001
25002     // private
25003     deferFocus : function(){
25004         this.focus.defer(10, this);
25005     },
25006
25007     // doc'ed in Field
25008     focus : function(){
25009         if(this.win && !this.sourceEditMode){
25010             this.win.focus();
25011         }else{
25012             this.el.focus();
25013         }
25014     },
25015     
25016     assignDocWin: function()
25017     {
25018         var iframe = this.iframe;
25019         
25020          if(Roo.isIE){
25021             this.doc = iframe.contentWindow.document;
25022             this.win = iframe.contentWindow;
25023         } else {
25024 //            if (!Roo.get(this.frameId)) {
25025 //                return;
25026 //            }
25027 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25028 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25029             
25030             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25031                 return;
25032             }
25033             
25034             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25035             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25036         }
25037     },
25038     
25039     // private
25040     initEditor : function(){
25041         //console.log("INIT EDITOR");
25042         this.assignDocWin();
25043         
25044         
25045         
25046         this.doc.designMode="on";
25047         this.doc.open();
25048         this.doc.write(this.getDocMarkup());
25049         this.doc.close();
25050         
25051         var dbody = (this.doc.body || this.doc.documentElement);
25052         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25053         // this copies styles from the containing element into thsi one..
25054         // not sure why we need all of this..
25055         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25056         
25057         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25058         //ss['background-attachment'] = 'fixed'; // w3c
25059         dbody.bgProperties = 'fixed'; // ie
25060         //Roo.DomHelper.applyStyles(dbody, ss);
25061         Roo.EventManager.on(this.doc, {
25062             //'mousedown': this.onEditorEvent,
25063             'mouseup': this.onEditorEvent,
25064             'dblclick': this.onEditorEvent,
25065             'click': this.onEditorEvent,
25066             'keyup': this.onEditorEvent,
25067             buffer:100,
25068             scope: this
25069         });
25070         if(Roo.isGecko){
25071             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25072         }
25073         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25074             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25075         }
25076         this.initialized = true;
25077
25078         this.owner.fireEvent('initialize', this);
25079         this.pushValue();
25080     },
25081
25082     // private
25083     onDestroy : function(){
25084         
25085         
25086         
25087         if(this.rendered){
25088             
25089             //for (var i =0; i < this.toolbars.length;i++) {
25090             //    // fixme - ask toolbars for heights?
25091             //    this.toolbars[i].onDestroy();
25092            // }
25093             
25094             //this.wrap.dom.innerHTML = '';
25095             //this.wrap.remove();
25096         }
25097     },
25098
25099     // private
25100     onFirstFocus : function(){
25101         
25102         this.assignDocWin();
25103         
25104         
25105         this.activated = true;
25106          
25107     
25108         if(Roo.isGecko){ // prevent silly gecko errors
25109             this.win.focus();
25110             var s = this.win.getSelection();
25111             if(!s.focusNode || s.focusNode.nodeType != 3){
25112                 var r = s.getRangeAt(0);
25113                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25114                 r.collapse(true);
25115                 this.deferFocus();
25116             }
25117             try{
25118                 this.execCmd('useCSS', true);
25119                 this.execCmd('styleWithCSS', false);
25120             }catch(e){}
25121         }
25122         this.owner.fireEvent('activate', this);
25123     },
25124
25125     // private
25126     adjustFont: function(btn){
25127         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25128         //if(Roo.isSafari){ // safari
25129         //    adjust *= 2;
25130        // }
25131         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25132         if(Roo.isSafari){ // safari
25133             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25134             v =  (v < 10) ? 10 : v;
25135             v =  (v > 48) ? 48 : v;
25136             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25137             
25138         }
25139         
25140         
25141         v = Math.max(1, v+adjust);
25142         
25143         this.execCmd('FontSize', v  );
25144     },
25145
25146     onEditorEvent : function(e){
25147         this.owner.fireEvent('editorevent', this, e);
25148       //  this.updateToolbar();
25149         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25150     },
25151
25152     insertTag : function(tg)
25153     {
25154         // could be a bit smarter... -> wrap the current selected tRoo..
25155         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25156             
25157             range = this.createRange(this.getSelection());
25158             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25159             wrappingNode.appendChild(range.extractContents());
25160             range.insertNode(wrappingNode);
25161
25162             return;
25163             
25164             
25165             
25166         }
25167         this.execCmd("formatblock",   tg);
25168         
25169     },
25170     
25171     insertText : function(txt)
25172     {
25173         
25174         
25175         var range = this.createRange();
25176         range.deleteContents();
25177                //alert(Sender.getAttribute('label'));
25178                
25179         range.insertNode(this.doc.createTextNode(txt));
25180     } ,
25181     
25182      
25183
25184     /**
25185      * Executes a Midas editor command on the editor document and performs necessary focus and
25186      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25187      * @param {String} cmd The Midas command
25188      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25189      */
25190     relayCmd : function(cmd, value){
25191         this.win.focus();
25192         this.execCmd(cmd, value);
25193         this.owner.fireEvent('editorevent', this);
25194         //this.updateToolbar();
25195         this.owner.deferFocus();
25196     },
25197
25198     /**
25199      * Executes a Midas editor command directly on the editor document.
25200      * For visual commands, you should use {@link #relayCmd} instead.
25201      * <b>This should only be called after the editor is initialized.</b>
25202      * @param {String} cmd The Midas command
25203      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25204      */
25205     execCmd : function(cmd, value){
25206         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25207         this.syncValue();
25208     },
25209  
25210  
25211    
25212     /**
25213      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25214      * to insert tRoo.
25215      * @param {String} text | dom node.. 
25216      */
25217     insertAtCursor : function(text)
25218     {
25219         
25220         
25221         
25222         if(!this.activated){
25223             return;
25224         }
25225         /*
25226         if(Roo.isIE){
25227             this.win.focus();
25228             var r = this.doc.selection.createRange();
25229             if(r){
25230                 r.collapse(true);
25231                 r.pasteHTML(text);
25232                 this.syncValue();
25233                 this.deferFocus();
25234             
25235             }
25236             return;
25237         }
25238         */
25239         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25240             this.win.focus();
25241             
25242             
25243             // from jquery ui (MIT licenced)
25244             var range, node;
25245             var win = this.win;
25246             
25247             if (win.getSelection && win.getSelection().getRangeAt) {
25248                 range = win.getSelection().getRangeAt(0);
25249                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25250                 range.insertNode(node);
25251             } else if (win.document.selection && win.document.selection.createRange) {
25252                 // no firefox support
25253                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25254                 win.document.selection.createRange().pasteHTML(txt);
25255             } else {
25256                 // no firefox support
25257                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25258                 this.execCmd('InsertHTML', txt);
25259             } 
25260             
25261             this.syncValue();
25262             
25263             this.deferFocus();
25264         }
25265     },
25266  // private
25267     mozKeyPress : function(e){
25268         if(e.ctrlKey){
25269             var c = e.getCharCode(), cmd;
25270           
25271             if(c > 0){
25272                 c = String.fromCharCode(c).toLowerCase();
25273                 switch(c){
25274                     case 'b':
25275                         cmd = 'bold';
25276                         break;
25277                     case 'i':
25278                         cmd = 'italic';
25279                         break;
25280                     
25281                     case 'u':
25282                         cmd = 'underline';
25283                         break;
25284                     
25285                     case 'v':
25286                         this.cleanUpPaste.defer(100, this);
25287                         return;
25288                         
25289                 }
25290                 if(cmd){
25291                     this.win.focus();
25292                     this.execCmd(cmd);
25293                     this.deferFocus();
25294                     e.preventDefault();
25295                 }
25296                 
25297             }
25298         }
25299     },
25300
25301     // private
25302     fixKeys : function(){ // load time branching for fastest keydown performance
25303         if(Roo.isIE){
25304             return function(e){
25305                 var k = e.getKey(), r;
25306                 if(k == e.TAB){
25307                     e.stopEvent();
25308                     r = this.doc.selection.createRange();
25309                     if(r){
25310                         r.collapse(true);
25311                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25312                         this.deferFocus();
25313                     }
25314                     return;
25315                 }
25316                 
25317                 if(k == e.ENTER){
25318                     r = this.doc.selection.createRange();
25319                     if(r){
25320                         var target = r.parentElement();
25321                         if(!target || target.tagName.toLowerCase() != 'li'){
25322                             e.stopEvent();
25323                             r.pasteHTML('<br />');
25324                             r.collapse(false);
25325                             r.select();
25326                         }
25327                     }
25328                 }
25329                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25330                     this.cleanUpPaste.defer(100, this);
25331                     return;
25332                 }
25333                 
25334                 
25335             };
25336         }else if(Roo.isOpera){
25337             return function(e){
25338                 var k = e.getKey();
25339                 if(k == e.TAB){
25340                     e.stopEvent();
25341                     this.win.focus();
25342                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25343                     this.deferFocus();
25344                 }
25345                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25346                     this.cleanUpPaste.defer(100, this);
25347                     return;
25348                 }
25349                 
25350             };
25351         }else if(Roo.isSafari){
25352             return function(e){
25353                 var k = e.getKey();
25354                 
25355                 if(k == e.TAB){
25356                     e.stopEvent();
25357                     this.execCmd('InsertText','\t');
25358                     this.deferFocus();
25359                     return;
25360                 }
25361                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25362                     this.cleanUpPaste.defer(100, this);
25363                     return;
25364                 }
25365                 
25366              };
25367         }
25368     }(),
25369     
25370     getAllAncestors: function()
25371     {
25372         var p = this.getSelectedNode();
25373         var a = [];
25374         if (!p) {
25375             a.push(p); // push blank onto stack..
25376             p = this.getParentElement();
25377         }
25378         
25379         
25380         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25381             a.push(p);
25382             p = p.parentNode;
25383         }
25384         a.push(this.doc.body);
25385         return a;
25386     },
25387     lastSel : false,
25388     lastSelNode : false,
25389     
25390     
25391     getSelection : function() 
25392     {
25393         this.assignDocWin();
25394         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25395     },
25396     
25397     getSelectedNode: function() 
25398     {
25399         // this may only work on Gecko!!!
25400         
25401         // should we cache this!!!!
25402         
25403         
25404         
25405          
25406         var range = this.createRange(this.getSelection()).cloneRange();
25407         
25408         if (Roo.isIE) {
25409             var parent = range.parentElement();
25410             while (true) {
25411                 var testRange = range.duplicate();
25412                 testRange.moveToElementText(parent);
25413                 if (testRange.inRange(range)) {
25414                     break;
25415                 }
25416                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25417                     break;
25418                 }
25419                 parent = parent.parentElement;
25420             }
25421             return parent;
25422         }
25423         
25424         // is ancestor a text element.
25425         var ac =  range.commonAncestorContainer;
25426         if (ac.nodeType == 3) {
25427             ac = ac.parentNode;
25428         }
25429         
25430         var ar = ac.childNodes;
25431          
25432         var nodes = [];
25433         var other_nodes = [];
25434         var has_other_nodes = false;
25435         for (var i=0;i<ar.length;i++) {
25436             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25437                 continue;
25438             }
25439             // fullly contained node.
25440             
25441             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25442                 nodes.push(ar[i]);
25443                 continue;
25444             }
25445             
25446             // probably selected..
25447             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25448                 other_nodes.push(ar[i]);
25449                 continue;
25450             }
25451             // outer..
25452             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25453                 continue;
25454             }
25455             
25456             
25457             has_other_nodes = true;
25458         }
25459         if (!nodes.length && other_nodes.length) {
25460             nodes= other_nodes;
25461         }
25462         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25463             return false;
25464         }
25465         
25466         return nodes[0];
25467     },
25468     createRange: function(sel)
25469     {
25470         // this has strange effects when using with 
25471         // top toolbar - not sure if it's a great idea.
25472         //this.editor.contentWindow.focus();
25473         if (typeof sel != "undefined") {
25474             try {
25475                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25476             } catch(e) {
25477                 return this.doc.createRange();
25478             }
25479         } else {
25480             return this.doc.createRange();
25481         }
25482     },
25483     getParentElement: function()
25484     {
25485         
25486         this.assignDocWin();
25487         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25488         
25489         var range = this.createRange(sel);
25490          
25491         try {
25492             var p = range.commonAncestorContainer;
25493             while (p.nodeType == 3) { // text node
25494                 p = p.parentNode;
25495             }
25496             return p;
25497         } catch (e) {
25498             return null;
25499         }
25500     
25501     },
25502     /***
25503      *
25504      * Range intersection.. the hard stuff...
25505      *  '-1' = before
25506      *  '0' = hits..
25507      *  '1' = after.
25508      *         [ -- selected range --- ]
25509      *   [fail]                        [fail]
25510      *
25511      *    basically..
25512      *      if end is before start or  hits it. fail.
25513      *      if start is after end or hits it fail.
25514      *
25515      *   if either hits (but other is outside. - then it's not 
25516      *   
25517      *    
25518      **/
25519     
25520     
25521     // @see http://www.thismuchiknow.co.uk/?p=64.
25522     rangeIntersectsNode : function(range, node)
25523     {
25524         var nodeRange = node.ownerDocument.createRange();
25525         try {
25526             nodeRange.selectNode(node);
25527         } catch (e) {
25528             nodeRange.selectNodeContents(node);
25529         }
25530     
25531         var rangeStartRange = range.cloneRange();
25532         rangeStartRange.collapse(true);
25533     
25534         var rangeEndRange = range.cloneRange();
25535         rangeEndRange.collapse(false);
25536     
25537         var nodeStartRange = nodeRange.cloneRange();
25538         nodeStartRange.collapse(true);
25539     
25540         var nodeEndRange = nodeRange.cloneRange();
25541         nodeEndRange.collapse(false);
25542     
25543         return rangeStartRange.compareBoundaryPoints(
25544                  Range.START_TO_START, nodeEndRange) == -1 &&
25545                rangeEndRange.compareBoundaryPoints(
25546                  Range.START_TO_START, nodeStartRange) == 1;
25547         
25548          
25549     },
25550     rangeCompareNode : function(range, node)
25551     {
25552         var nodeRange = node.ownerDocument.createRange();
25553         try {
25554             nodeRange.selectNode(node);
25555         } catch (e) {
25556             nodeRange.selectNodeContents(node);
25557         }
25558         
25559         
25560         range.collapse(true);
25561     
25562         nodeRange.collapse(true);
25563      
25564         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25565         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25566          
25567         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25568         
25569         var nodeIsBefore   =  ss == 1;
25570         var nodeIsAfter    = ee == -1;
25571         
25572         if (nodeIsBefore && nodeIsAfter)
25573             return 0; // outer
25574         if (!nodeIsBefore && nodeIsAfter)
25575             return 1; //right trailed.
25576         
25577         if (nodeIsBefore && !nodeIsAfter)
25578             return 2;  // left trailed.
25579         // fully contined.
25580         return 3;
25581     },
25582
25583     // private? - in a new class?
25584     cleanUpPaste :  function()
25585     {
25586         // cleans up the whole document..
25587         Roo.log('cleanuppaste');
25588         
25589         this.cleanUpChildren(this.doc.body);
25590         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25591         if (clean != this.doc.body.innerHTML) {
25592             this.doc.body.innerHTML = clean;
25593         }
25594         
25595     },
25596     
25597     cleanWordChars : function(input) {// change the chars to hex code
25598         var he = Roo.HtmlEditorCore;
25599         
25600         var output = input;
25601         Roo.each(he.swapCodes, function(sw) { 
25602             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25603             
25604             output = output.replace(swapper, sw[1]);
25605         });
25606         
25607         return output;
25608     },
25609     
25610     
25611     cleanUpChildren : function (n)
25612     {
25613         if (!n.childNodes.length) {
25614             return;
25615         }
25616         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25617            this.cleanUpChild(n.childNodes[i]);
25618         }
25619     },
25620     
25621     
25622         
25623     
25624     cleanUpChild : function (node)
25625     {
25626         var ed = this;
25627         //console.log(node);
25628         if (node.nodeName == "#text") {
25629             // clean up silly Windows -- stuff?
25630             return; 
25631         }
25632         if (node.nodeName == "#comment") {
25633             node.parentNode.removeChild(node);
25634             // clean up silly Windows -- stuff?
25635             return; 
25636         }
25637         var lcname = node.tagName.toLowerCase();
25638         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25639         // whitelist of tags..
25640         
25641         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25642             // remove node.
25643             node.parentNode.removeChild(node);
25644             return;
25645             
25646         }
25647         
25648         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25649         
25650         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25651         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25652         
25653         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25654         //    remove_keep_children = true;
25655         //}
25656         
25657         if (remove_keep_children) {
25658             this.cleanUpChildren(node);
25659             // inserts everything just before this node...
25660             while (node.childNodes.length) {
25661                 var cn = node.childNodes[0];
25662                 node.removeChild(cn);
25663                 node.parentNode.insertBefore(cn, node);
25664             }
25665             node.parentNode.removeChild(node);
25666             return;
25667         }
25668         
25669         if (!node.attributes || !node.attributes.length) {
25670             this.cleanUpChildren(node);
25671             return;
25672         }
25673         
25674         function cleanAttr(n,v)
25675         {
25676             
25677             if (v.match(/^\./) || v.match(/^\//)) {
25678                 return;
25679             }
25680             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25681                 return;
25682             }
25683             if (v.match(/^#/)) {
25684                 return;
25685             }
25686 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25687             node.removeAttribute(n);
25688             
25689         }
25690         
25691         var cwhite = this.cwhite;
25692         var cblack = this.cblack;
25693             
25694         function cleanStyle(n,v)
25695         {
25696             if (v.match(/expression/)) { //XSS?? should we even bother..
25697                 node.removeAttribute(n);
25698                 return;
25699             }
25700             
25701             var parts = v.split(/;/);
25702             var clean = [];
25703             
25704             Roo.each(parts, function(p) {
25705                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25706                 if (!p.length) {
25707                     return true;
25708                 }
25709                 var l = p.split(':').shift().replace(/\s+/g,'');
25710                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25711                 
25712                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25713 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25714                     //node.removeAttribute(n);
25715                     return true;
25716                 }
25717                 //Roo.log()
25718                 // only allow 'c whitelisted system attributes'
25719                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25720 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25721                     //node.removeAttribute(n);
25722                     return true;
25723                 }
25724                 
25725                 
25726                  
25727                 
25728                 clean.push(p);
25729                 return true;
25730             });
25731             if (clean.length) { 
25732                 node.setAttribute(n, clean.join(';'));
25733             } else {
25734                 node.removeAttribute(n);
25735             }
25736             
25737         }
25738         
25739         
25740         for (var i = node.attributes.length-1; i > -1 ; i--) {
25741             var a = node.attributes[i];
25742             //console.log(a);
25743             
25744             if (a.name.toLowerCase().substr(0,2)=='on')  {
25745                 node.removeAttribute(a.name);
25746                 continue;
25747             }
25748             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25749                 node.removeAttribute(a.name);
25750                 continue;
25751             }
25752             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25753                 cleanAttr(a.name,a.value); // fixme..
25754                 continue;
25755             }
25756             if (a.name == 'style') {
25757                 cleanStyle(a.name,a.value);
25758                 continue;
25759             }
25760             /// clean up MS crap..
25761             // tecnically this should be a list of valid class'es..
25762             
25763             
25764             if (a.name == 'class') {
25765                 if (a.value.match(/^Mso/)) {
25766                     node.className = '';
25767                 }
25768                 
25769                 if (a.value.match(/body/)) {
25770                     node.className = '';
25771                 }
25772                 continue;
25773             }
25774             
25775             // style cleanup!?
25776             // class cleanup?
25777             
25778         }
25779         
25780         
25781         this.cleanUpChildren(node);
25782         
25783         
25784     },
25785     /**
25786      * Clean up MS wordisms...
25787      */
25788     cleanWord : function(node)
25789     {
25790         var _t = this;
25791         var cleanWordChildren = function()
25792         {
25793             if (!node.childNodes.length) {
25794                 return;
25795             }
25796             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25797                _t.cleanWord(node.childNodes[i]);
25798             }
25799         }
25800         
25801         
25802         if (!node) {
25803             this.cleanWord(this.doc.body);
25804             return;
25805         }
25806         if (node.nodeName == "#text") {
25807             // clean up silly Windows -- stuff?
25808             return; 
25809         }
25810         if (node.nodeName == "#comment") {
25811             node.parentNode.removeChild(node);
25812             // clean up silly Windows -- stuff?
25813             return; 
25814         }
25815         
25816         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25817             node.parentNode.removeChild(node);
25818             return;
25819         }
25820         
25821         // remove - but keep children..
25822         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25823             while (node.childNodes.length) {
25824                 var cn = node.childNodes[0];
25825                 node.removeChild(cn);
25826                 node.parentNode.insertBefore(cn, node);
25827             }
25828             node.parentNode.removeChild(node);
25829             cleanWordChildren();
25830             return;
25831         }
25832         // clean styles
25833         if (node.className.length) {
25834             
25835             var cn = node.className.split(/\W+/);
25836             var cna = [];
25837             Roo.each(cn, function(cls) {
25838                 if (cls.match(/Mso[a-zA-Z]+/)) {
25839                     return;
25840                 }
25841                 cna.push(cls);
25842             });
25843             node.className = cna.length ? cna.join(' ') : '';
25844             if (!cna.length) {
25845                 node.removeAttribute("class");
25846             }
25847         }
25848         
25849         if (node.hasAttribute("lang")) {
25850             node.removeAttribute("lang");
25851         }
25852         
25853         if (node.hasAttribute("style")) {
25854             
25855             var styles = node.getAttribute("style").split(";");
25856             var nstyle = [];
25857             Roo.each(styles, function(s) {
25858                 if (!s.match(/:/)) {
25859                     return;
25860                 }
25861                 var kv = s.split(":");
25862                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25863                     return;
25864                 }
25865                 // what ever is left... we allow.
25866                 nstyle.push(s);
25867             });
25868             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25869             if (!nstyle.length) {
25870                 node.removeAttribute('style');
25871             }
25872         }
25873         
25874         cleanWordChildren();
25875         
25876         
25877     },
25878     domToHTML : function(currentElement, depth, nopadtext) {
25879         
25880         depth = depth || 0;
25881         nopadtext = nopadtext || false;
25882     
25883         if (!currentElement) {
25884             return this.domToHTML(this.doc.body);
25885         }
25886         
25887         //Roo.log(currentElement);
25888         var j;
25889         var allText = false;
25890         var nodeName = currentElement.nodeName;
25891         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25892         
25893         if  (nodeName == '#text') {
25894             return currentElement.nodeValue;
25895         }
25896         
25897         
25898         var ret = '';
25899         if (nodeName != 'BODY') {
25900              
25901             var i = 0;
25902             // Prints the node tagName, such as <A>, <IMG>, etc
25903             if (tagName) {
25904                 var attr = [];
25905                 for(i = 0; i < currentElement.attributes.length;i++) {
25906                     // quoting?
25907                     var aname = currentElement.attributes.item(i).name;
25908                     if (!currentElement.attributes.item(i).value.length) {
25909                         continue;
25910                     }
25911                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25912                 }
25913                 
25914                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25915             } 
25916             else {
25917                 
25918                 // eack
25919             }
25920         } else {
25921             tagName = false;
25922         }
25923         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25924             return ret;
25925         }
25926         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25927             nopadtext = true;
25928         }
25929         
25930         
25931         // Traverse the tree
25932         i = 0;
25933         var currentElementChild = currentElement.childNodes.item(i);
25934         var allText = true;
25935         var innerHTML  = '';
25936         lastnode = '';
25937         while (currentElementChild) {
25938             // Formatting code (indent the tree so it looks nice on the screen)
25939             var nopad = nopadtext;
25940             if (lastnode == 'SPAN') {
25941                 nopad  = true;
25942             }
25943             // text
25944             if  (currentElementChild.nodeName == '#text') {
25945                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25946                 if (!nopad && toadd.length > 80) {
25947                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25948                 }
25949                 innerHTML  += toadd;
25950                 
25951                 i++;
25952                 currentElementChild = currentElement.childNodes.item(i);
25953                 lastNode = '';
25954                 continue;
25955             }
25956             allText = false;
25957             
25958             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25959                 
25960             // Recursively traverse the tree structure of the child node
25961             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25962             lastnode = currentElementChild.nodeName;
25963             i++;
25964             currentElementChild=currentElement.childNodes.item(i);
25965         }
25966         
25967         ret += innerHTML;
25968         
25969         if (!allText) {
25970                 // The remaining code is mostly for formatting the tree
25971             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25972         }
25973         
25974         
25975         if (tagName) {
25976             ret+= "</"+tagName+">";
25977         }
25978         return ret;
25979         
25980     },
25981         
25982     applyBlacklists : function()
25983     {
25984         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25985         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25986         
25987         this.white = [];
25988         this.black = [];
25989         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25990             if (b.indexOf(tag) > -1) {
25991                 return;
25992             }
25993             this.white.push(tag);
25994             
25995         }, this);
25996         
25997         Roo.each(w, function(tag) {
25998             if (b.indexOf(tag) > -1) {
25999                 return;
26000             }
26001             if (this.white.indexOf(tag) > -1) {
26002                 return;
26003             }
26004             this.white.push(tag);
26005             
26006         }, this);
26007         
26008         
26009         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26010             if (w.indexOf(tag) > -1) {
26011                 return;
26012             }
26013             this.black.push(tag);
26014             
26015         }, this);
26016         
26017         Roo.each(b, function(tag) {
26018             if (w.indexOf(tag) > -1) {
26019                 return;
26020             }
26021             if (this.black.indexOf(tag) > -1) {
26022                 return;
26023             }
26024             this.black.push(tag);
26025             
26026         }, this);
26027         
26028         
26029         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26030         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26031         
26032         this.cwhite = [];
26033         this.cblack = [];
26034         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26035             if (b.indexOf(tag) > -1) {
26036                 return;
26037             }
26038             this.cwhite.push(tag);
26039             
26040         }, this);
26041         
26042         Roo.each(w, function(tag) {
26043             if (b.indexOf(tag) > -1) {
26044                 return;
26045             }
26046             if (this.cwhite.indexOf(tag) > -1) {
26047                 return;
26048             }
26049             this.cwhite.push(tag);
26050             
26051         }, this);
26052         
26053         
26054         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26055             if (w.indexOf(tag) > -1) {
26056                 return;
26057             }
26058             this.cblack.push(tag);
26059             
26060         }, this);
26061         
26062         Roo.each(b, function(tag) {
26063             if (w.indexOf(tag) > -1) {
26064                 return;
26065             }
26066             if (this.cblack.indexOf(tag) > -1) {
26067                 return;
26068             }
26069             this.cblack.push(tag);
26070             
26071         }, this);
26072     }
26073     
26074     // hide stuff that is not compatible
26075     /**
26076      * @event blur
26077      * @hide
26078      */
26079     /**
26080      * @event change
26081      * @hide
26082      */
26083     /**
26084      * @event focus
26085      * @hide
26086      */
26087     /**
26088      * @event specialkey
26089      * @hide
26090      */
26091     /**
26092      * @cfg {String} fieldClass @hide
26093      */
26094     /**
26095      * @cfg {String} focusClass @hide
26096      */
26097     /**
26098      * @cfg {String} autoCreate @hide
26099      */
26100     /**
26101      * @cfg {String} inputType @hide
26102      */
26103     /**
26104      * @cfg {String} invalidClass @hide
26105      */
26106     /**
26107      * @cfg {String} invalidText @hide
26108      */
26109     /**
26110      * @cfg {String} msgFx @hide
26111      */
26112     /**
26113      * @cfg {String} validateOnBlur @hide
26114      */
26115 });
26116
26117 Roo.HtmlEditorCore.white = [
26118         'area', 'br', 'img', 'input', 'hr', 'wbr',
26119         
26120        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26121        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26122        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26123        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26124        'table',   'ul',         'xmp', 
26125        
26126        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26127       'thead',   'tr', 
26128      
26129       'dir', 'menu', 'ol', 'ul', 'dl',
26130        
26131       'embed',  'object'
26132 ];
26133
26134
26135 Roo.HtmlEditorCore.black = [
26136     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26137         'applet', // 
26138         'base',   'basefont', 'bgsound', 'blink',  'body', 
26139         'frame',  'frameset', 'head',    'html',   'ilayer', 
26140         'iframe', 'layer',  'link',     'meta',    'object',   
26141         'script', 'style' ,'title',  'xml' // clean later..
26142 ];
26143 Roo.HtmlEditorCore.clean = [
26144     'script', 'style', 'title', 'xml'
26145 ];
26146 Roo.HtmlEditorCore.remove = [
26147     'font'
26148 ];
26149 // attributes..
26150
26151 Roo.HtmlEditorCore.ablack = [
26152     'on'
26153 ];
26154     
26155 Roo.HtmlEditorCore.aclean = [ 
26156     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26157 ];
26158
26159 // protocols..
26160 Roo.HtmlEditorCore.pwhite= [
26161         'http',  'https',  'mailto'
26162 ];
26163
26164 // white listed style attributes.
26165 Roo.HtmlEditorCore.cwhite= [
26166       //  'text-align', /// default is to allow most things..
26167       
26168          
26169 //        'font-size'//??
26170 ];
26171
26172 // black listed style attributes.
26173 Roo.HtmlEditorCore.cblack= [
26174       //  'font-size' -- this can be set by the project 
26175 ];
26176
26177
26178 Roo.HtmlEditorCore.swapCodes   =[ 
26179     [    8211, "--" ], 
26180     [    8212, "--" ], 
26181     [    8216,  "'" ],  
26182     [    8217, "'" ],  
26183     [    8220, '"' ],  
26184     [    8221, '"' ],  
26185     [    8226, "*" ],  
26186     [    8230, "..." ]
26187 ]; 
26188
26189     //<script type="text/javascript">
26190
26191 /*
26192  * Ext JS Library 1.1.1
26193  * Copyright(c) 2006-2007, Ext JS, LLC.
26194  * Licence LGPL
26195  * 
26196  */
26197  
26198  
26199 Roo.form.HtmlEditor = function(config){
26200     
26201     
26202     
26203     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26204     
26205     if (!this.toolbars) {
26206         this.toolbars = [];
26207     }
26208     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26209     
26210     
26211 };
26212
26213 /**
26214  * @class Roo.form.HtmlEditor
26215  * @extends Roo.form.Field
26216  * Provides a lightweight HTML Editor component.
26217  *
26218  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26219  * 
26220  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26221  * supported by this editor.</b><br/><br/>
26222  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26223  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26224  */
26225 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26226     /**
26227      * @cfg {Boolean} clearUp
26228      */
26229     clearUp : true,
26230       /**
26231      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26232      */
26233     toolbars : false,
26234    
26235      /**
26236      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26237      *                        Roo.resizable.
26238      */
26239     resizable : false,
26240      /**
26241      * @cfg {Number} height (in pixels)
26242      */   
26243     height: 300,
26244    /**
26245      * @cfg {Number} width (in pixels)
26246      */   
26247     width: 500,
26248     
26249     /**
26250      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26251      * 
26252      */
26253     stylesheets: false,
26254     
26255     
26256      /**
26257      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26258      * 
26259      */
26260     cblack: false,
26261     /**
26262      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26263      * 
26264      */
26265     cwhite: false,
26266     
26267      /**
26268      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26269      * 
26270      */
26271     black: false,
26272     /**
26273      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26274      * 
26275      */
26276     white: false,
26277     
26278     // id of frame..
26279     frameId: false,
26280     
26281     // private properties
26282     validationEvent : false,
26283     deferHeight: true,
26284     initialized : false,
26285     activated : false,
26286     
26287     onFocus : Roo.emptyFn,
26288     iframePad:3,
26289     hideMode:'offsets',
26290     
26291     actionMode : 'container', // defaults to hiding it...
26292     
26293     defaultAutoCreate : { // modified by initCompnoent..
26294         tag: "textarea",
26295         style:"width:500px;height:300px;",
26296         autocomplete: "off"
26297     },
26298
26299     // private
26300     initComponent : function(){
26301         this.addEvents({
26302             /**
26303              * @event initialize
26304              * Fires when the editor is fully initialized (including the iframe)
26305              * @param {HtmlEditor} this
26306              */
26307             initialize: true,
26308             /**
26309              * @event activate
26310              * Fires when the editor is first receives the focus. Any insertion must wait
26311              * until after this event.
26312              * @param {HtmlEditor} this
26313              */
26314             activate: true,
26315              /**
26316              * @event beforesync
26317              * Fires before the textarea is updated with content from the editor iframe. Return false
26318              * to cancel the sync.
26319              * @param {HtmlEditor} this
26320              * @param {String} html
26321              */
26322             beforesync: true,
26323              /**
26324              * @event beforepush
26325              * Fires before the iframe editor is updated with content from the textarea. Return false
26326              * to cancel the push.
26327              * @param {HtmlEditor} this
26328              * @param {String} html
26329              */
26330             beforepush: true,
26331              /**
26332              * @event sync
26333              * Fires when the textarea is updated with content from the editor iframe.
26334              * @param {HtmlEditor} this
26335              * @param {String} html
26336              */
26337             sync: true,
26338              /**
26339              * @event push
26340              * Fires when the iframe editor is updated with content from the textarea.
26341              * @param {HtmlEditor} this
26342              * @param {String} html
26343              */
26344             push: true,
26345              /**
26346              * @event editmodechange
26347              * Fires when the editor switches edit modes
26348              * @param {HtmlEditor} this
26349              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26350              */
26351             editmodechange: true,
26352             /**
26353              * @event editorevent
26354              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26355              * @param {HtmlEditor} this
26356              */
26357             editorevent: true,
26358             /**
26359              * @event firstfocus
26360              * Fires when on first focus - needed by toolbars..
26361              * @param {HtmlEditor} this
26362              */
26363             firstfocus: true,
26364             /**
26365              * @event autosave
26366              * Auto save the htmlEditor value as a file into Events
26367              * @param {HtmlEditor} this
26368              */
26369             autosave: true,
26370             /**
26371              * @event savedpreview
26372              * preview the saved version of htmlEditor
26373              * @param {HtmlEditor} this
26374              */
26375             savedpreview: true
26376         });
26377         this.defaultAutoCreate =  {
26378             tag: "textarea",
26379             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26380             autocomplete: "off"
26381         };
26382     },
26383
26384     /**
26385      * Protected method that will not generally be called directly. It
26386      * is called when the editor creates its toolbar. Override this method if you need to
26387      * add custom toolbar buttons.
26388      * @param {HtmlEditor} editor
26389      */
26390     createToolbar : function(editor){
26391         Roo.log("create toolbars");
26392         if (!editor.toolbars || !editor.toolbars.length) {
26393             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26394         }
26395         
26396         for (var i =0 ; i < editor.toolbars.length;i++) {
26397             editor.toolbars[i] = Roo.factory(
26398                     typeof(editor.toolbars[i]) == 'string' ?
26399                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26400                 Roo.form.HtmlEditor);
26401             editor.toolbars[i].init(editor);
26402         }
26403          
26404         
26405     },
26406
26407      
26408     // private
26409     onRender : function(ct, position)
26410     {
26411         var _t = this;
26412         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26413         
26414         this.wrap = this.el.wrap({
26415             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26416         });
26417         
26418         this.editorcore.onRender(ct, position);
26419          
26420         if (this.resizable) {
26421             this.resizeEl = new Roo.Resizable(this.wrap, {
26422                 pinned : true,
26423                 wrap: true,
26424                 dynamic : true,
26425                 minHeight : this.height,
26426                 height: this.height,
26427                 handles : this.resizable,
26428                 width: this.width,
26429                 listeners : {
26430                     resize : function(r, w, h) {
26431                         _t.onResize(w,h); // -something
26432                     }
26433                 }
26434             });
26435             
26436         }
26437         this.createToolbar(this);
26438        
26439         
26440         if(!this.width){
26441             this.setSize(this.wrap.getSize());
26442         }
26443         if (this.resizeEl) {
26444             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26445             // should trigger onReize..
26446         }
26447         
26448 //        if(this.autosave && this.w){
26449 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26450 //        }
26451     },
26452
26453     // private
26454     onResize : function(w, h)
26455     {
26456         //Roo.log('resize: ' +w + ',' + h );
26457         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26458         var ew = false;
26459         var eh = false;
26460         
26461         if(this.el ){
26462             if(typeof w == 'number'){
26463                 var aw = w - this.wrap.getFrameWidth('lr');
26464                 this.el.setWidth(this.adjustWidth('textarea', aw));
26465                 ew = aw;
26466             }
26467             if(typeof h == 'number'){
26468                 var tbh = 0;
26469                 for (var i =0; i < this.toolbars.length;i++) {
26470                     // fixme - ask toolbars for heights?
26471                     tbh += this.toolbars[i].tb.el.getHeight();
26472                     if (this.toolbars[i].footer) {
26473                         tbh += this.toolbars[i].footer.el.getHeight();
26474                     }
26475                 }
26476                 
26477                 
26478                 
26479                 
26480                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26481                 ah -= 5; // knock a few pixes off for look..
26482                 this.el.setHeight(this.adjustWidth('textarea', ah));
26483                 var eh = ah;
26484             }
26485         }
26486         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26487         this.editorcore.onResize(ew,eh);
26488         
26489     },
26490
26491     /**
26492      * Toggles the editor between standard and source edit mode.
26493      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26494      */
26495     toggleSourceEdit : function(sourceEditMode)
26496     {
26497         this.editorcore.toggleSourceEdit(sourceEditMode);
26498         
26499         if(this.editorcore.sourceEditMode){
26500             Roo.log('editor - showing textarea');
26501             
26502 //            Roo.log('in');
26503 //            Roo.log(this.syncValue());
26504             this.editorcore.syncValue();
26505             this.el.removeClass('x-hidden');
26506             this.el.dom.removeAttribute('tabIndex');
26507             this.el.focus();
26508         }else{
26509             Roo.log('editor - hiding textarea');
26510 //            Roo.log('out')
26511 //            Roo.log(this.pushValue()); 
26512             this.editorcore.pushValue();
26513             
26514             this.el.addClass('x-hidden');
26515             this.el.dom.setAttribute('tabIndex', -1);
26516             //this.deferFocus();
26517         }
26518          
26519         this.setSize(this.wrap.getSize());
26520         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26521     },
26522  
26523     // private (for BoxComponent)
26524     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26525
26526     // private (for BoxComponent)
26527     getResizeEl : function(){
26528         return this.wrap;
26529     },
26530
26531     // private (for BoxComponent)
26532     getPositionEl : function(){
26533         return this.wrap;
26534     },
26535
26536     // private
26537     initEvents : function(){
26538         this.originalValue = this.getValue();
26539     },
26540
26541     /**
26542      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26543      * @method
26544      */
26545     markInvalid : Roo.emptyFn,
26546     /**
26547      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26548      * @method
26549      */
26550     clearInvalid : Roo.emptyFn,
26551
26552     setValue : function(v){
26553         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26554         this.editorcore.pushValue();
26555     },
26556
26557      
26558     // private
26559     deferFocus : function(){
26560         this.focus.defer(10, this);
26561     },
26562
26563     // doc'ed in Field
26564     focus : function(){
26565         this.editorcore.focus();
26566         
26567     },
26568       
26569
26570     // private
26571     onDestroy : function(){
26572         
26573         
26574         
26575         if(this.rendered){
26576             
26577             for (var i =0; i < this.toolbars.length;i++) {
26578                 // fixme - ask toolbars for heights?
26579                 this.toolbars[i].onDestroy();
26580             }
26581             
26582             this.wrap.dom.innerHTML = '';
26583             this.wrap.remove();
26584         }
26585     },
26586
26587     // private
26588     onFirstFocus : function(){
26589         //Roo.log("onFirstFocus");
26590         this.editorcore.onFirstFocus();
26591          for (var i =0; i < this.toolbars.length;i++) {
26592             this.toolbars[i].onFirstFocus();
26593         }
26594         
26595     },
26596     
26597     // private
26598     syncValue : function()
26599     {
26600         this.editorcore.syncValue();
26601     },
26602     
26603     pushValue : function()
26604     {
26605         this.editorcore.pushValue();
26606     }
26607      
26608     
26609     // hide stuff that is not compatible
26610     /**
26611      * @event blur
26612      * @hide
26613      */
26614     /**
26615      * @event change
26616      * @hide
26617      */
26618     /**
26619      * @event focus
26620      * @hide
26621      */
26622     /**
26623      * @event specialkey
26624      * @hide
26625      */
26626     /**
26627      * @cfg {String} fieldClass @hide
26628      */
26629     /**
26630      * @cfg {String} focusClass @hide
26631      */
26632     /**
26633      * @cfg {String} autoCreate @hide
26634      */
26635     /**
26636      * @cfg {String} inputType @hide
26637      */
26638     /**
26639      * @cfg {String} invalidClass @hide
26640      */
26641     /**
26642      * @cfg {String} invalidText @hide
26643      */
26644     /**
26645      * @cfg {String} msgFx @hide
26646      */
26647     /**
26648      * @cfg {String} validateOnBlur @hide
26649      */
26650 });
26651  
26652     // <script type="text/javascript">
26653 /*
26654  * Based on
26655  * Ext JS Library 1.1.1
26656  * Copyright(c) 2006-2007, Ext JS, LLC.
26657  *  
26658  
26659  */
26660
26661 /**
26662  * @class Roo.form.HtmlEditorToolbar1
26663  * Basic Toolbar
26664  * 
26665  * Usage:
26666  *
26667  new Roo.form.HtmlEditor({
26668     ....
26669     toolbars : [
26670         new Roo.form.HtmlEditorToolbar1({
26671             disable : { fonts: 1 , format: 1, ..., ... , ...],
26672             btns : [ .... ]
26673         })
26674     }
26675      
26676  * 
26677  * @cfg {Object} disable List of elements to disable..
26678  * @cfg {Array} btns List of additional buttons.
26679  * 
26680  * 
26681  * NEEDS Extra CSS? 
26682  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26683  */
26684  
26685 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26686 {
26687     
26688     Roo.apply(this, config);
26689     
26690     // default disabled, based on 'good practice'..
26691     this.disable = this.disable || {};
26692     Roo.applyIf(this.disable, {
26693         fontSize : true,
26694         colors : true,
26695         specialElements : true
26696     });
26697     
26698     
26699     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26700     // dont call parent... till later.
26701 }
26702
26703 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26704     
26705     tb: false,
26706     
26707     rendered: false,
26708     
26709     editor : false,
26710     editorcore : false,
26711     /**
26712      * @cfg {Object} disable  List of toolbar elements to disable
26713          
26714      */
26715     disable : false,
26716     
26717     
26718      /**
26719      * @cfg {String} createLinkText The default text for the create link prompt
26720      */
26721     createLinkText : 'Please enter the URL for the link:',
26722     /**
26723      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26724      */
26725     defaultLinkValue : 'http:/'+'/',
26726    
26727     
26728       /**
26729      * @cfg {Array} fontFamilies An array of available font families
26730      */
26731     fontFamilies : [
26732         'Arial',
26733         'Courier New',
26734         'Tahoma',
26735         'Times New Roman',
26736         'Verdana'
26737     ],
26738     
26739     specialChars : [
26740            "&#169;",
26741           "&#174;",     
26742           "&#8482;",    
26743           "&#163;" ,    
26744          // "&#8212;",    
26745           "&#8230;",    
26746           "&#247;" ,    
26747         //  "&#225;" ,     ?? a acute?
26748            "&#8364;"    , //Euro
26749        //   "&#8220;"    ,
26750         //  "&#8221;"    ,
26751         //  "&#8226;"    ,
26752           "&#176;"  //   , // degrees
26753
26754          // "&#233;"     , // e ecute
26755          // "&#250;"     , // u ecute?
26756     ],
26757     
26758     specialElements : [
26759         {
26760             text: "Insert Table",
26761             xtype: 'MenuItem',
26762             xns : Roo.Menu,
26763             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26764                 
26765         },
26766         {    
26767             text: "Insert Image",
26768             xtype: 'MenuItem',
26769             xns : Roo.Menu,
26770             ihtml : '<img src="about:blank"/>'
26771             
26772         }
26773         
26774          
26775     ],
26776     
26777     
26778     inputElements : [ 
26779             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26780             "input:submit", "input:button", "select", "textarea", "label" ],
26781     formats : [
26782         ["p"] ,  
26783         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26784         ["pre"],[ "code"], 
26785         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26786         ['div'],['span']
26787     ],
26788     
26789     cleanStyles : [
26790         "font-size"
26791     ],
26792      /**
26793      * @cfg {String} defaultFont default font to use.
26794      */
26795     defaultFont: 'tahoma',
26796    
26797     fontSelect : false,
26798     
26799     
26800     formatCombo : false,
26801     
26802     init : function(editor)
26803     {
26804         this.editor = editor;
26805         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26806         var editorcore = this.editorcore;
26807         
26808         var _t = this;
26809         
26810         var fid = editorcore.frameId;
26811         var etb = this;
26812         function btn(id, toggle, handler){
26813             var xid = fid + '-'+ id ;
26814             return {
26815                 id : xid,
26816                 cmd : id,
26817                 cls : 'x-btn-icon x-edit-'+id,
26818                 enableToggle:toggle !== false,
26819                 scope: _t, // was editor...
26820                 handler:handler||_t.relayBtnCmd,
26821                 clickEvent:'mousedown',
26822                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26823                 tabIndex:-1
26824             };
26825         }
26826         
26827         
26828         
26829         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26830         this.tb = tb;
26831          // stop form submits
26832         tb.el.on('click', function(e){
26833             e.preventDefault(); // what does this do?
26834         });
26835
26836         if(!this.disable.font) { // && !Roo.isSafari){
26837             /* why no safari for fonts 
26838             editor.fontSelect = tb.el.createChild({
26839                 tag:'select',
26840                 tabIndex: -1,
26841                 cls:'x-font-select',
26842                 html: this.createFontOptions()
26843             });
26844             
26845             editor.fontSelect.on('change', function(){
26846                 var font = editor.fontSelect.dom.value;
26847                 editor.relayCmd('fontname', font);
26848                 editor.deferFocus();
26849             }, editor);
26850             
26851             tb.add(
26852                 editor.fontSelect.dom,
26853                 '-'
26854             );
26855             */
26856             
26857         };
26858         if(!this.disable.formats){
26859             this.formatCombo = new Roo.form.ComboBox({
26860                 store: new Roo.data.SimpleStore({
26861                     id : 'tag',
26862                     fields: ['tag'],
26863                     data : this.formats // from states.js
26864                 }),
26865                 blockFocus : true,
26866                 name : '',
26867                 //autoCreate : {tag: "div",  size: "20"},
26868                 displayField:'tag',
26869                 typeAhead: false,
26870                 mode: 'local',
26871                 editable : false,
26872                 triggerAction: 'all',
26873                 emptyText:'Add tag',
26874                 selectOnFocus:true,
26875                 width:135,
26876                 listeners : {
26877                     'select': function(c, r, i) {
26878                         editorcore.insertTag(r.get('tag'));
26879                         editor.focus();
26880                     }
26881                 }
26882
26883             });
26884             tb.addField(this.formatCombo);
26885             
26886         }
26887         
26888         if(!this.disable.format){
26889             tb.add(
26890                 btn('bold'),
26891                 btn('italic'),
26892                 btn('underline')
26893             );
26894         };
26895         if(!this.disable.fontSize){
26896             tb.add(
26897                 '-',
26898                 
26899                 
26900                 btn('increasefontsize', false, editorcore.adjustFont),
26901                 btn('decreasefontsize', false, editorcore.adjustFont)
26902             );
26903         };
26904         
26905         
26906         if(!this.disable.colors){
26907             tb.add(
26908                 '-', {
26909                     id:editorcore.frameId +'-forecolor',
26910                     cls:'x-btn-icon x-edit-forecolor',
26911                     clickEvent:'mousedown',
26912                     tooltip: this.buttonTips['forecolor'] || undefined,
26913                     tabIndex:-1,
26914                     menu : new Roo.menu.ColorMenu({
26915                         allowReselect: true,
26916                         focus: Roo.emptyFn,
26917                         value:'000000',
26918                         plain:true,
26919                         selectHandler: function(cp, color){
26920                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26921                             editor.deferFocus();
26922                         },
26923                         scope: editorcore,
26924                         clickEvent:'mousedown'
26925                     })
26926                 }, {
26927                     id:editorcore.frameId +'backcolor',
26928                     cls:'x-btn-icon x-edit-backcolor',
26929                     clickEvent:'mousedown',
26930                     tooltip: this.buttonTips['backcolor'] || undefined,
26931                     tabIndex:-1,
26932                     menu : new Roo.menu.ColorMenu({
26933                         focus: Roo.emptyFn,
26934                         value:'FFFFFF',
26935                         plain:true,
26936                         allowReselect: true,
26937                         selectHandler: function(cp, color){
26938                             if(Roo.isGecko){
26939                                 editorcore.execCmd('useCSS', false);
26940                                 editorcore.execCmd('hilitecolor', color);
26941                                 editorcore.execCmd('useCSS', true);
26942                                 editor.deferFocus();
26943                             }else{
26944                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26945                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26946                                 editor.deferFocus();
26947                             }
26948                         },
26949                         scope:editorcore,
26950                         clickEvent:'mousedown'
26951                     })
26952                 }
26953             );
26954         };
26955         // now add all the items...
26956         
26957
26958         if(!this.disable.alignments){
26959             tb.add(
26960                 '-',
26961                 btn('justifyleft'),
26962                 btn('justifycenter'),
26963                 btn('justifyright')
26964             );
26965         };
26966
26967         //if(!Roo.isSafari){
26968             if(!this.disable.links){
26969                 tb.add(
26970                     '-',
26971                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26972                 );
26973             };
26974
26975             if(!this.disable.lists){
26976                 tb.add(
26977                     '-',
26978                     btn('insertorderedlist'),
26979                     btn('insertunorderedlist')
26980                 );
26981             }
26982             if(!this.disable.sourceEdit){
26983                 tb.add(
26984                     '-',
26985                     btn('sourceedit', true, function(btn){
26986                         Roo.log(this);
26987                         this.toggleSourceEdit(btn.pressed);
26988                     })
26989                 );
26990             }
26991         //}
26992         
26993         var smenu = { };
26994         // special menu.. - needs to be tidied up..
26995         if (!this.disable.special) {
26996             smenu = {
26997                 text: "&#169;",
26998                 cls: 'x-edit-none',
26999                 
27000                 menu : {
27001                     items : []
27002                 }
27003             };
27004             for (var i =0; i < this.specialChars.length; i++) {
27005                 smenu.menu.items.push({
27006                     
27007                     html: this.specialChars[i],
27008                     handler: function(a,b) {
27009                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27010                         //editor.insertAtCursor(a.html);
27011                         
27012                     },
27013                     tabIndex:-1
27014                 });
27015             }
27016             
27017             
27018             tb.add(smenu);
27019             
27020             
27021         }
27022         
27023         var cmenu = { };
27024         if (!this.disable.cleanStyles) {
27025             cmenu = {
27026                 cls: 'x-btn-icon x-btn-clear',
27027                 
27028                 menu : {
27029                     items : []
27030                 }
27031             };
27032             for (var i =0; i < this.cleanStyles.length; i++) {
27033                 cmenu.menu.items.push({
27034                     actiontype : this.cleanStyles[i],
27035                     html: 'Remove ' + this.cleanStyles[i],
27036                     handler: function(a,b) {
27037                         Roo.log(a);
27038                         Roo.log(b);
27039                         var c = Roo.get(editorcore.doc.body);
27040                         c.select('[style]').each(function(s) {
27041                             s.dom.style.removeProperty(a.actiontype);
27042                         });
27043                         editorcore.syncValue();
27044                     },
27045                     tabIndex:-1
27046                 });
27047             }
27048             cmenu.menu.items.push({
27049                 actiontype : 'word',
27050                 html: 'Remove MS Word Formating',
27051                 handler: function(a,b) {
27052                     editorcore.cleanWord();
27053                     editorcore.syncValue();
27054                 },
27055                 tabIndex:-1
27056             });
27057             
27058             cmenu.menu.items.push({
27059                 actiontype : 'all',
27060                 html: 'Remove All Styles',
27061                 handler: function(a,b) {
27062                     
27063                     var c = Roo.get(editorcore.doc.body);
27064                     c.select('[style]').each(function(s) {
27065                         s.dom.removeAttribute('style');
27066                     });
27067                     editorcore.syncValue();
27068                 },
27069                 tabIndex:-1
27070             });
27071              cmenu.menu.items.push({
27072                 actiontype : 'word',
27073                 html: 'Tidy HTML Source',
27074                 handler: function(a,b) {
27075                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27076                     editorcore.syncValue();
27077                 },
27078                 tabIndex:-1
27079             });
27080             
27081             
27082             tb.add(cmenu);
27083         }
27084          
27085         if (!this.disable.specialElements) {
27086             var semenu = {
27087                 text: "Other;",
27088                 cls: 'x-edit-none',
27089                 menu : {
27090                     items : []
27091                 }
27092             };
27093             for (var i =0; i < this.specialElements.length; i++) {
27094                 semenu.menu.items.push(
27095                     Roo.apply({ 
27096                         handler: function(a,b) {
27097                             editor.insertAtCursor(this.ihtml);
27098                         }
27099                     }, this.specialElements[i])
27100                 );
27101                     
27102             }
27103             
27104             tb.add(semenu);
27105             
27106             
27107         }
27108          
27109         
27110         if (this.btns) {
27111             for(var i =0; i< this.btns.length;i++) {
27112                 var b = Roo.factory(this.btns[i],Roo.form);
27113                 b.cls =  'x-edit-none';
27114                 b.scope = editorcore;
27115                 tb.add(b);
27116             }
27117         
27118         }
27119         
27120         
27121         
27122         // disable everything...
27123         
27124         this.tb.items.each(function(item){
27125            if(item.id != editorcore.frameId+ '-sourceedit'){
27126                 item.disable();
27127             }
27128         });
27129         this.rendered = true;
27130         
27131         // the all the btns;
27132         editor.on('editorevent', this.updateToolbar, this);
27133         // other toolbars need to implement this..
27134         //editor.on('editmodechange', this.updateToolbar, this);
27135     },
27136     
27137     
27138     relayBtnCmd : function(btn) {
27139         this.editorcore.relayCmd(btn.cmd);
27140     },
27141     // private used internally
27142     createLink : function(){
27143         Roo.log("create link?");
27144         var url = prompt(this.createLinkText, this.defaultLinkValue);
27145         if(url && url != 'http:/'+'/'){
27146             this.editorcore.relayCmd('createlink', url);
27147         }
27148     },
27149
27150     
27151     /**
27152      * Protected method that will not generally be called directly. It triggers
27153      * a toolbar update by reading the markup state of the current selection in the editor.
27154      */
27155     updateToolbar: function(){
27156
27157         if(!this.editorcore.activated){
27158             this.editor.onFirstFocus();
27159             return;
27160         }
27161
27162         var btns = this.tb.items.map, 
27163             doc = this.editorcore.doc,
27164             frameId = this.editorcore.frameId;
27165
27166         if(!this.disable.font && !Roo.isSafari){
27167             /*
27168             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27169             if(name != this.fontSelect.dom.value){
27170                 this.fontSelect.dom.value = name;
27171             }
27172             */
27173         }
27174         if(!this.disable.format){
27175             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27176             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27177             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27178         }
27179         if(!this.disable.alignments){
27180             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27181             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27182             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27183         }
27184         if(!Roo.isSafari && !this.disable.lists){
27185             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27186             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27187         }
27188         
27189         var ans = this.editorcore.getAllAncestors();
27190         if (this.formatCombo) {
27191             
27192             
27193             var store = this.formatCombo.store;
27194             this.formatCombo.setValue("");
27195             for (var i =0; i < ans.length;i++) {
27196                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27197                     // select it..
27198                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27199                     break;
27200                 }
27201             }
27202         }
27203         
27204         
27205         
27206         // hides menus... - so this cant be on a menu...
27207         Roo.menu.MenuMgr.hideAll();
27208
27209         //this.editorsyncValue();
27210     },
27211    
27212     
27213     createFontOptions : function(){
27214         var buf = [], fs = this.fontFamilies, ff, lc;
27215         
27216         
27217         
27218         for(var i = 0, len = fs.length; i< len; i++){
27219             ff = fs[i];
27220             lc = ff.toLowerCase();
27221             buf.push(
27222                 '<option value="',lc,'" style="font-family:',ff,';"',
27223                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27224                     ff,
27225                 '</option>'
27226             );
27227         }
27228         return buf.join('');
27229     },
27230     
27231     toggleSourceEdit : function(sourceEditMode){
27232         
27233         Roo.log("toolbar toogle");
27234         if(sourceEditMode === undefined){
27235             sourceEditMode = !this.sourceEditMode;
27236         }
27237         this.sourceEditMode = sourceEditMode === true;
27238         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27239         // just toggle the button?
27240         if(btn.pressed !== this.sourceEditMode){
27241             btn.toggle(this.sourceEditMode);
27242             return;
27243         }
27244         
27245         if(sourceEditMode){
27246             Roo.log("disabling buttons");
27247             this.tb.items.each(function(item){
27248                 if(item.cmd != 'sourceedit'){
27249                     item.disable();
27250                 }
27251             });
27252           
27253         }else{
27254             Roo.log("enabling buttons");
27255             if(this.editorcore.initialized){
27256                 this.tb.items.each(function(item){
27257                     item.enable();
27258                 });
27259             }
27260             
27261         }
27262         Roo.log("calling toggole on editor");
27263         // tell the editor that it's been pressed..
27264         this.editor.toggleSourceEdit(sourceEditMode);
27265        
27266     },
27267      /**
27268      * Object collection of toolbar tooltips for the buttons in the editor. The key
27269      * is the command id associated with that button and the value is a valid QuickTips object.
27270      * For example:
27271 <pre><code>
27272 {
27273     bold : {
27274         title: 'Bold (Ctrl+B)',
27275         text: 'Make the selected text bold.',
27276         cls: 'x-html-editor-tip'
27277     },
27278     italic : {
27279         title: 'Italic (Ctrl+I)',
27280         text: 'Make the selected text italic.',
27281         cls: 'x-html-editor-tip'
27282     },
27283     ...
27284 </code></pre>
27285     * @type Object
27286      */
27287     buttonTips : {
27288         bold : {
27289             title: 'Bold (Ctrl+B)',
27290             text: 'Make the selected text bold.',
27291             cls: 'x-html-editor-tip'
27292         },
27293         italic : {
27294             title: 'Italic (Ctrl+I)',
27295             text: 'Make the selected text italic.',
27296             cls: 'x-html-editor-tip'
27297         },
27298         underline : {
27299             title: 'Underline (Ctrl+U)',
27300             text: 'Underline the selected text.',
27301             cls: 'x-html-editor-tip'
27302         },
27303         increasefontsize : {
27304             title: 'Grow Text',
27305             text: 'Increase the font size.',
27306             cls: 'x-html-editor-tip'
27307         },
27308         decreasefontsize : {
27309             title: 'Shrink Text',
27310             text: 'Decrease the font size.',
27311             cls: 'x-html-editor-tip'
27312         },
27313         backcolor : {
27314             title: 'Text Highlight Color',
27315             text: 'Change the background color of the selected text.',
27316             cls: 'x-html-editor-tip'
27317         },
27318         forecolor : {
27319             title: 'Font Color',
27320             text: 'Change the color of the selected text.',
27321             cls: 'x-html-editor-tip'
27322         },
27323         justifyleft : {
27324             title: 'Align Text Left',
27325             text: 'Align text to the left.',
27326             cls: 'x-html-editor-tip'
27327         },
27328         justifycenter : {
27329             title: 'Center Text',
27330             text: 'Center text in the editor.',
27331             cls: 'x-html-editor-tip'
27332         },
27333         justifyright : {
27334             title: 'Align Text Right',
27335             text: 'Align text to the right.',
27336             cls: 'x-html-editor-tip'
27337         },
27338         insertunorderedlist : {
27339             title: 'Bullet List',
27340             text: 'Start a bulleted list.',
27341             cls: 'x-html-editor-tip'
27342         },
27343         insertorderedlist : {
27344             title: 'Numbered List',
27345             text: 'Start a numbered list.',
27346             cls: 'x-html-editor-tip'
27347         },
27348         createlink : {
27349             title: 'Hyperlink',
27350             text: 'Make the selected text a hyperlink.',
27351             cls: 'x-html-editor-tip'
27352         },
27353         sourceedit : {
27354             title: 'Source Edit',
27355             text: 'Switch to source editing mode.',
27356             cls: 'x-html-editor-tip'
27357         }
27358     },
27359     // private
27360     onDestroy : function(){
27361         if(this.rendered){
27362             
27363             this.tb.items.each(function(item){
27364                 if(item.menu){
27365                     item.menu.removeAll();
27366                     if(item.menu.el){
27367                         item.menu.el.destroy();
27368                     }
27369                 }
27370                 item.destroy();
27371             });
27372              
27373         }
27374     },
27375     onFirstFocus: function() {
27376         this.tb.items.each(function(item){
27377            item.enable();
27378         });
27379     }
27380 });
27381
27382
27383
27384
27385 // <script type="text/javascript">
27386 /*
27387  * Based on
27388  * Ext JS Library 1.1.1
27389  * Copyright(c) 2006-2007, Ext JS, LLC.
27390  *  
27391  
27392  */
27393
27394  
27395 /**
27396  * @class Roo.form.HtmlEditor.ToolbarContext
27397  * Context Toolbar
27398  * 
27399  * Usage:
27400  *
27401  new Roo.form.HtmlEditor({
27402     ....
27403     toolbars : [
27404         { xtype: 'ToolbarStandard', styles : {} }
27405         { xtype: 'ToolbarContext', disable : {} }
27406     ]
27407 })
27408
27409      
27410  * 
27411  * @config : {Object} disable List of elements to disable.. (not done yet.)
27412  * @config : {Object} styles  Map of styles available.
27413  * 
27414  */
27415
27416 Roo.form.HtmlEditor.ToolbarContext = function(config)
27417 {
27418     
27419     Roo.apply(this, config);
27420     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27421     // dont call parent... till later.
27422     this.styles = this.styles || {};
27423 }
27424
27425  
27426
27427 Roo.form.HtmlEditor.ToolbarContext.types = {
27428     'IMG' : {
27429         width : {
27430             title: "Width",
27431             width: 40
27432         },
27433         height:  {
27434             title: "Height",
27435             width: 40
27436         },
27437         align: {
27438             title: "Align",
27439             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27440             width : 80
27441             
27442         },
27443         border: {
27444             title: "Border",
27445             width: 40
27446         },
27447         alt: {
27448             title: "Alt",
27449             width: 120
27450         },
27451         src : {
27452             title: "Src",
27453             width: 220
27454         }
27455         
27456     },
27457     'A' : {
27458         name : {
27459             title: "Name",
27460             width: 50
27461         },
27462         target:  {
27463             title: "Target",
27464             width: 120
27465         },
27466         href:  {
27467             title: "Href",
27468             width: 220
27469         } // border?
27470         
27471     },
27472     'TABLE' : {
27473         rows : {
27474             title: "Rows",
27475             width: 20
27476         },
27477         cols : {
27478             title: "Cols",
27479             width: 20
27480         },
27481         width : {
27482             title: "Width",
27483             width: 40
27484         },
27485         height : {
27486             title: "Height",
27487             width: 40
27488         },
27489         border : {
27490             title: "Border",
27491             width: 20
27492         }
27493     },
27494     'TD' : {
27495         width : {
27496             title: "Width",
27497             width: 40
27498         },
27499         height : {
27500             title: "Height",
27501             width: 40
27502         },   
27503         align: {
27504             title: "Align",
27505             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27506             width: 80
27507         },
27508         valign: {
27509             title: "Valign",
27510             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27511             width: 80
27512         },
27513         colspan: {
27514             title: "Colspan",
27515             width: 20
27516             
27517         },
27518          'font-family'  : {
27519             title : "Font",
27520             style : 'fontFamily',
27521             displayField: 'display',
27522             optname : 'font-family',
27523             width: 140
27524         }
27525     },
27526     'INPUT' : {
27527         name : {
27528             title: "name",
27529             width: 120
27530         },
27531         value : {
27532             title: "Value",
27533             width: 120
27534         },
27535         width : {
27536             title: "Width",
27537             width: 40
27538         }
27539     },
27540     'LABEL' : {
27541         'for' : {
27542             title: "For",
27543             width: 120
27544         }
27545     },
27546     'TEXTAREA' : {
27547           name : {
27548             title: "name",
27549             width: 120
27550         },
27551         rows : {
27552             title: "Rows",
27553             width: 20
27554         },
27555         cols : {
27556             title: "Cols",
27557             width: 20
27558         }
27559     },
27560     'SELECT' : {
27561         name : {
27562             title: "name",
27563             width: 120
27564         },
27565         selectoptions : {
27566             title: "Options",
27567             width: 200
27568         }
27569     },
27570     
27571     // should we really allow this??
27572     // should this just be 
27573     'BODY' : {
27574         title : {
27575             title: "Title",
27576             width: 200,
27577             disabled : true
27578         }
27579     },
27580     'SPAN' : {
27581         'font-family'  : {
27582             title : "Font",
27583             style : 'fontFamily',
27584             displayField: 'display',
27585             optname : 'font-family',
27586             width: 140
27587         }
27588     },
27589     'DIV' : {
27590         'font-family'  : {
27591             title : "Font",
27592             style : 'fontFamily',
27593             displayField: 'display',
27594             optname : 'font-family',
27595             width: 140
27596         }
27597     },
27598      'P' : {
27599         'font-family'  : {
27600             title : "Font",
27601             style : 'fontFamily',
27602             displayField: 'display',
27603             optname : 'font-family',
27604             width: 140
27605         }
27606     },
27607     
27608     '*' : {
27609         // empty..
27610     }
27611
27612 };
27613
27614 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27615 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27616
27617 Roo.form.HtmlEditor.ToolbarContext.options = {
27618         'font-family'  : [ 
27619                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27620                 [ 'Courier New', 'Courier New'],
27621                 [ 'Tahoma', 'Tahoma'],
27622                 [ 'Times New Roman,serif', 'Times'],
27623                 [ 'Verdana','Verdana' ]
27624         ]
27625 };
27626
27627 // fixme - these need to be configurable..
27628  
27629
27630 Roo.form.HtmlEditor.ToolbarContext.types
27631
27632
27633 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27634     
27635     tb: false,
27636     
27637     rendered: false,
27638     
27639     editor : false,
27640     editorcore : false,
27641     /**
27642      * @cfg {Object} disable  List of toolbar elements to disable
27643          
27644      */
27645     disable : false,
27646     /**
27647      * @cfg {Object} styles List of styles 
27648      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27649      *
27650      * These must be defined in the page, so they get rendered correctly..
27651      * .headline { }
27652      * TD.underline { }
27653      * 
27654      */
27655     styles : false,
27656     
27657     options: false,
27658     
27659     toolbars : false,
27660     
27661     init : function(editor)
27662     {
27663         this.editor = editor;
27664         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27665         var editorcore = this.editorcore;
27666         
27667         var fid = editorcore.frameId;
27668         var etb = this;
27669         function btn(id, toggle, handler){
27670             var xid = fid + '-'+ id ;
27671             return {
27672                 id : xid,
27673                 cmd : id,
27674                 cls : 'x-btn-icon x-edit-'+id,
27675                 enableToggle:toggle !== false,
27676                 scope: editorcore, // was editor...
27677                 handler:handler||editorcore.relayBtnCmd,
27678                 clickEvent:'mousedown',
27679                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27680                 tabIndex:-1
27681             };
27682         }
27683         // create a new element.
27684         var wdiv = editor.wrap.createChild({
27685                 tag: 'div'
27686             }, editor.wrap.dom.firstChild.nextSibling, true);
27687         
27688         // can we do this more than once??
27689         
27690          // stop form submits
27691       
27692  
27693         // disable everything...
27694         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27695         this.toolbars = {};
27696            
27697         for (var i in  ty) {
27698           
27699             this.toolbars[i] = this.buildToolbar(ty[i],i);
27700         }
27701         this.tb = this.toolbars.BODY;
27702         this.tb.el.show();
27703         this.buildFooter();
27704         this.footer.show();
27705         editor.on('hide', function( ) { this.footer.hide() }, this);
27706         editor.on('show', function( ) { this.footer.show() }, this);
27707         
27708          
27709         this.rendered = true;
27710         
27711         // the all the btns;
27712         editor.on('editorevent', this.updateToolbar, this);
27713         // other toolbars need to implement this..
27714         //editor.on('editmodechange', this.updateToolbar, this);
27715     },
27716     
27717     
27718     
27719     /**
27720      * Protected method that will not generally be called directly. It triggers
27721      * a toolbar update by reading the markup state of the current selection in the editor.
27722      */
27723     updateToolbar: function(editor,ev,sel){
27724
27725         //Roo.log(ev);
27726         // capture mouse up - this is handy for selecting images..
27727         // perhaps should go somewhere else...
27728         if(!this.editorcore.activated){
27729              this.editor.onFirstFocus();
27730             return;
27731         }
27732         
27733         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27734         // selectNode - might want to handle IE?
27735         if (ev &&
27736             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27737             ev.target && ev.target.tagName == 'IMG') {
27738             // they have click on an image...
27739             // let's see if we can change the selection...
27740             sel = ev.target;
27741          
27742               var nodeRange = sel.ownerDocument.createRange();
27743             try {
27744                 nodeRange.selectNode(sel);
27745             } catch (e) {
27746                 nodeRange.selectNodeContents(sel);
27747             }
27748             //nodeRange.collapse(true);
27749             var s = this.editorcore.win.getSelection();
27750             s.removeAllRanges();
27751             s.addRange(nodeRange);
27752         }  
27753         
27754       
27755         var updateFooter = sel ? false : true;
27756         
27757         
27758         var ans = this.editorcore.getAllAncestors();
27759         
27760         // pick
27761         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27762         
27763         if (!sel) { 
27764             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27765             sel = sel ? sel : this.editorcore.doc.body;
27766             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27767             
27768         }
27769         // pick a menu that exists..
27770         var tn = sel.tagName.toUpperCase();
27771         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27772         
27773         tn = sel.tagName.toUpperCase();
27774         
27775         var lastSel = this.tb.selectedNode
27776         
27777         this.tb.selectedNode = sel;
27778         
27779         // if current menu does not match..
27780         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27781                 
27782             this.tb.el.hide();
27783             ///console.log("show: " + tn);
27784             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27785             this.tb.el.show();
27786             // update name
27787             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27788             
27789             
27790             // update attributes
27791             if (this.tb.fields) {
27792                 this.tb.fields.each(function(e) {
27793                     if (e.stylename) {
27794                         e.setValue(sel.style[e.stylename]);
27795                         return;
27796                     } 
27797                    e.setValue(sel.getAttribute(e.attrname));
27798                 });
27799             }
27800             
27801             var hasStyles = false;
27802             for(var i in this.styles) {
27803                 hasStyles = true;
27804                 break;
27805             }
27806             
27807             // update styles
27808             if (hasStyles) { 
27809                 var st = this.tb.fields.item(0);
27810                 
27811                 st.store.removeAll();
27812                
27813                 
27814                 var cn = sel.className.split(/\s+/);
27815                 
27816                 var avs = [];
27817                 if (this.styles['*']) {
27818                     
27819                     Roo.each(this.styles['*'], function(v) {
27820                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27821                     });
27822                 }
27823                 if (this.styles[tn]) { 
27824                     Roo.each(this.styles[tn], function(v) {
27825                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27826                     });
27827                 }
27828                 
27829                 st.store.loadData(avs);
27830                 st.collapse();
27831                 st.setValue(cn);
27832             }
27833             // flag our selected Node.
27834             this.tb.selectedNode = sel;
27835            
27836            
27837             Roo.menu.MenuMgr.hideAll();
27838
27839         }
27840         
27841         if (!updateFooter) {
27842             //this.footDisp.dom.innerHTML = ''; 
27843             return;
27844         }
27845         // update the footer
27846         //
27847         var html = '';
27848         
27849         this.footerEls = ans.reverse();
27850         Roo.each(this.footerEls, function(a,i) {
27851             if (!a) { return; }
27852             html += html.length ? ' &gt; '  :  '';
27853             
27854             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27855             
27856         });
27857        
27858         // 
27859         var sz = this.footDisp.up('td').getSize();
27860         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27861         this.footDisp.dom.style.marginLeft = '5px';
27862         
27863         this.footDisp.dom.style.overflow = 'hidden';
27864         
27865         this.footDisp.dom.innerHTML = html;
27866             
27867         //this.editorsyncValue();
27868     },
27869      
27870     
27871    
27872        
27873     // private
27874     onDestroy : function(){
27875         if(this.rendered){
27876             
27877             this.tb.items.each(function(item){
27878                 if(item.menu){
27879                     item.menu.removeAll();
27880                     if(item.menu.el){
27881                         item.menu.el.destroy();
27882                     }
27883                 }
27884                 item.destroy();
27885             });
27886              
27887         }
27888     },
27889     onFirstFocus: function() {
27890         // need to do this for all the toolbars..
27891         this.tb.items.each(function(item){
27892            item.enable();
27893         });
27894     },
27895     buildToolbar: function(tlist, nm)
27896     {
27897         var editor = this.editor;
27898         var editorcore = this.editorcore;
27899          // create a new element.
27900         var wdiv = editor.wrap.createChild({
27901                 tag: 'div'
27902             }, editor.wrap.dom.firstChild.nextSibling, true);
27903         
27904        
27905         var tb = new Roo.Toolbar(wdiv);
27906         // add the name..
27907         
27908         tb.add(nm+ ":&nbsp;");
27909         
27910         var styles = [];
27911         for(var i in this.styles) {
27912             styles.push(i);
27913         }
27914         
27915         // styles...
27916         if (styles && styles.length) {
27917             
27918             // this needs a multi-select checkbox...
27919             tb.addField( new Roo.form.ComboBox({
27920                 store: new Roo.data.SimpleStore({
27921                     id : 'val',
27922                     fields: ['val', 'selected'],
27923                     data : [] 
27924                 }),
27925                 name : '-roo-edit-className',
27926                 attrname : 'className',
27927                 displayField: 'val',
27928                 typeAhead: false,
27929                 mode: 'local',
27930                 editable : false,
27931                 triggerAction: 'all',
27932                 emptyText:'Select Style',
27933                 selectOnFocus:true,
27934                 width: 130,
27935                 listeners : {
27936                     'select': function(c, r, i) {
27937                         // initial support only for on class per el..
27938                         tb.selectedNode.className =  r ? r.get('val') : '';
27939                         editorcore.syncValue();
27940                     }
27941                 }
27942     
27943             }));
27944         }
27945         
27946         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27947         var tbops = tbc.options;
27948         
27949         for (var i in tlist) {
27950             
27951             var item = tlist[i];
27952             tb.add(item.title + ":&nbsp;");
27953             
27954             
27955             //optname == used so you can configure the options available..
27956             var opts = item.opts ? item.opts : false;
27957             if (item.optname) {
27958                 opts = tbops[item.optname];
27959            
27960             }
27961             
27962             if (opts) {
27963                 // opts == pulldown..
27964                 tb.addField( new Roo.form.ComboBox({
27965                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27966                         id : 'val',
27967                         fields: ['val', 'display'],
27968                         data : opts  
27969                     }),
27970                     name : '-roo-edit-' + i,
27971                     attrname : i,
27972                     stylename : item.style ? item.style : false,
27973                     displayField: item.displayField ? item.displayField : 'val',
27974                     valueField :  'val',
27975                     typeAhead: false,
27976                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27977                     editable : false,
27978                     triggerAction: 'all',
27979                     emptyText:'Select',
27980                     selectOnFocus:true,
27981                     width: item.width ? item.width  : 130,
27982                     listeners : {
27983                         'select': function(c, r, i) {
27984                             if (c.stylename) {
27985                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27986                                 return;
27987                             }
27988                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27989                         }
27990                     }
27991
27992                 }));
27993                 continue;
27994                     
27995                  
27996                 
27997                 tb.addField( new Roo.form.TextField({
27998                     name: i,
27999                     width: 100,
28000                     //allowBlank:false,
28001                     value: ''
28002                 }));
28003                 continue;
28004             }
28005             tb.addField( new Roo.form.TextField({
28006                 name: '-roo-edit-' + i,
28007                 attrname : i,
28008                 
28009                 width: item.width,
28010                 //allowBlank:true,
28011                 value: '',
28012                 listeners: {
28013                     'change' : function(f, nv, ov) {
28014                         tb.selectedNode.setAttribute(f.attrname, nv);
28015                     }
28016                 }
28017             }));
28018              
28019         }
28020         tb.addFill();
28021         var _this = this;
28022         tb.addButton( {
28023             text: 'Remove Tag',
28024     
28025             listeners : {
28026                 click : function ()
28027                 {
28028                     // remove
28029                     // undo does not work.
28030                      
28031                     var sn = tb.selectedNode;
28032                     
28033                     var pn = sn.parentNode;
28034                     
28035                     var stn =  sn.childNodes[0];
28036                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28037                     while (sn.childNodes.length) {
28038                         var node = sn.childNodes[0];
28039                         sn.removeChild(node);
28040                         //Roo.log(node);
28041                         pn.insertBefore(node, sn);
28042                         
28043                     }
28044                     pn.removeChild(sn);
28045                     var range = editorcore.createRange();
28046         
28047                     range.setStart(stn,0);
28048                     range.setEnd(en,0); //????
28049                     //range.selectNode(sel);
28050                     
28051                     
28052                     var selection = editorcore.getSelection();
28053                     selection.removeAllRanges();
28054                     selection.addRange(range);
28055                     
28056                     
28057                     
28058                     //_this.updateToolbar(null, null, pn);
28059                     _this.updateToolbar(null, null, null);
28060                     _this.footDisp.dom.innerHTML = ''; 
28061                 }
28062             }
28063             
28064                     
28065                 
28066             
28067         });
28068         
28069         
28070         tb.el.on('click', function(e){
28071             e.preventDefault(); // what does this do?
28072         });
28073         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28074         tb.el.hide();
28075         tb.name = nm;
28076         // dont need to disable them... as they will get hidden
28077         return tb;
28078          
28079         
28080     },
28081     buildFooter : function()
28082     {
28083         
28084         var fel = this.editor.wrap.createChild();
28085         this.footer = new Roo.Toolbar(fel);
28086         // toolbar has scrolly on left / right?
28087         var footDisp= new Roo.Toolbar.Fill();
28088         var _t = this;
28089         this.footer.add(
28090             {
28091                 text : '&lt;',
28092                 xtype: 'Button',
28093                 handler : function() {
28094                     _t.footDisp.scrollTo('left',0,true)
28095                 }
28096             }
28097         );
28098         this.footer.add( footDisp );
28099         this.footer.add( 
28100             {
28101                 text : '&gt;',
28102                 xtype: 'Button',
28103                 handler : function() {
28104                     // no animation..
28105                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28106                 }
28107             }
28108         );
28109         var fel = Roo.get(footDisp.el);
28110         fel.addClass('x-editor-context');
28111         this.footDispWrap = fel; 
28112         this.footDispWrap.overflow  = 'hidden';
28113         
28114         this.footDisp = fel.createChild();
28115         this.footDispWrap.on('click', this.onContextClick, this)
28116         
28117         
28118     },
28119     onContextClick : function (ev,dom)
28120     {
28121         ev.preventDefault();
28122         var  cn = dom.className;
28123         //Roo.log(cn);
28124         if (!cn.match(/x-ed-loc-/)) {
28125             return;
28126         }
28127         var n = cn.split('-').pop();
28128         var ans = this.footerEls;
28129         var sel = ans[n];
28130         
28131          // pick
28132         var range = this.editorcore.createRange();
28133         
28134         range.selectNodeContents(sel);
28135         //range.selectNode(sel);
28136         
28137         
28138         var selection = this.editorcore.getSelection();
28139         selection.removeAllRanges();
28140         selection.addRange(range);
28141         
28142         
28143         
28144         this.updateToolbar(null, null, sel);
28145         
28146         
28147     }
28148     
28149     
28150     
28151     
28152     
28153 });
28154
28155
28156
28157
28158
28159 /*
28160  * Based on:
28161  * Ext JS Library 1.1.1
28162  * Copyright(c) 2006-2007, Ext JS, LLC.
28163  *
28164  * Originally Released Under LGPL - original licence link has changed is not relivant.
28165  *
28166  * Fork - LGPL
28167  * <script type="text/javascript">
28168  */
28169  
28170 /**
28171  * @class Roo.form.BasicForm
28172  * @extends Roo.util.Observable
28173  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28174  * @constructor
28175  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28176  * @param {Object} config Configuration options
28177  */
28178 Roo.form.BasicForm = function(el, config){
28179     this.allItems = [];
28180     this.childForms = [];
28181     Roo.apply(this, config);
28182     /*
28183      * The Roo.form.Field items in this form.
28184      * @type MixedCollection
28185      */
28186      
28187      
28188     this.items = new Roo.util.MixedCollection(false, function(o){
28189         return o.id || (o.id = Roo.id());
28190     });
28191     this.addEvents({
28192         /**
28193          * @event beforeaction
28194          * Fires before any action is performed. Return false to cancel the action.
28195          * @param {Form} this
28196          * @param {Action} action The action to be performed
28197          */
28198         beforeaction: true,
28199         /**
28200          * @event actionfailed
28201          * Fires when an action fails.
28202          * @param {Form} this
28203          * @param {Action} action The action that failed
28204          */
28205         actionfailed : true,
28206         /**
28207          * @event actioncomplete
28208          * Fires when an action is completed.
28209          * @param {Form} this
28210          * @param {Action} action The action that completed
28211          */
28212         actioncomplete : true
28213     });
28214     if(el){
28215         this.initEl(el);
28216     }
28217     Roo.form.BasicForm.superclass.constructor.call(this);
28218 };
28219
28220 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28221     /**
28222      * @cfg {String} method
28223      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28224      */
28225     /**
28226      * @cfg {DataReader} reader
28227      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28228      * This is optional as there is built-in support for processing JSON.
28229      */
28230     /**
28231      * @cfg {DataReader} errorReader
28232      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28233      * This is completely optional as there is built-in support for processing JSON.
28234      */
28235     /**
28236      * @cfg {String} url
28237      * The URL to use for form actions if one isn't supplied in the action options.
28238      */
28239     /**
28240      * @cfg {Boolean} fileUpload
28241      * Set to true if this form is a file upload.
28242      */
28243      
28244     /**
28245      * @cfg {Object} baseParams
28246      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28247      */
28248      /**
28249      
28250     /**
28251      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28252      */
28253     timeout: 30,
28254
28255     // private
28256     activeAction : null,
28257
28258     /**
28259      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28260      * or setValues() data instead of when the form was first created.
28261      */
28262     trackResetOnLoad : false,
28263     
28264     
28265     /**
28266      * childForms - used for multi-tab forms
28267      * @type {Array}
28268      */
28269     childForms : false,
28270     
28271     /**
28272      * allItems - full list of fields.
28273      * @type {Array}
28274      */
28275     allItems : false,
28276     
28277     /**
28278      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28279      * element by passing it or its id or mask the form itself by passing in true.
28280      * @type Mixed
28281      */
28282     waitMsgTarget : false,
28283
28284     // private
28285     initEl : function(el){
28286         this.el = Roo.get(el);
28287         this.id = this.el.id || Roo.id();
28288         this.el.on('submit', this.onSubmit, this);
28289         this.el.addClass('x-form');
28290     },
28291
28292     // private
28293     onSubmit : function(e){
28294         e.stopEvent();
28295     },
28296
28297     /**
28298      * Returns true if client-side validation on the form is successful.
28299      * @return Boolean
28300      */
28301     isValid : function(){
28302         var valid = true;
28303         this.items.each(function(f){
28304            if(!f.validate()){
28305                valid = false;
28306            }
28307         });
28308         return valid;
28309     },
28310
28311     /**
28312      * Returns true if any fields in this form have changed since their original load.
28313      * @return Boolean
28314      */
28315     isDirty : function(){
28316         var dirty = false;
28317         this.items.each(function(f){
28318            if(f.isDirty()){
28319                dirty = true;
28320                return false;
28321            }
28322         });
28323         return dirty;
28324     },
28325
28326     /**
28327      * Performs a predefined action (submit or load) or custom actions you define on this form.
28328      * @param {String} actionName The name of the action type
28329      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28330      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28331      * accept other config options):
28332      * <pre>
28333 Property          Type             Description
28334 ----------------  ---------------  ----------------------------------------------------------------------------------
28335 url               String           The url for the action (defaults to the form's url)
28336 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28337 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28338 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28339                                    validate the form on the client (defaults to false)
28340      * </pre>
28341      * @return {BasicForm} this
28342      */
28343     doAction : function(action, options){
28344         if(typeof action == 'string'){
28345             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28346         }
28347         if(this.fireEvent('beforeaction', this, action) !== false){
28348             this.beforeAction(action);
28349             action.run.defer(100, action);
28350         }
28351         return this;
28352     },
28353
28354     /**
28355      * Shortcut to do a submit action.
28356      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28357      * @return {BasicForm} this
28358      */
28359     submit : function(options){
28360         this.doAction('submit', options);
28361         return this;
28362     },
28363
28364     /**
28365      * Shortcut to do a load action.
28366      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28367      * @return {BasicForm} this
28368      */
28369     load : function(options){
28370         this.doAction('load', options);
28371         return this;
28372     },
28373
28374     /**
28375      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28376      * @param {Record} record The record to edit
28377      * @return {BasicForm} this
28378      */
28379     updateRecord : function(record){
28380         record.beginEdit();
28381         var fs = record.fields;
28382         fs.each(function(f){
28383             var field = this.findField(f.name);
28384             if(field){
28385                 record.set(f.name, field.getValue());
28386             }
28387         }, this);
28388         record.endEdit();
28389         return this;
28390     },
28391
28392     /**
28393      * Loads an Roo.data.Record into this form.
28394      * @param {Record} record The record to load
28395      * @return {BasicForm} this
28396      */
28397     loadRecord : function(record){
28398         this.setValues(record.data);
28399         return this;
28400     },
28401
28402     // private
28403     beforeAction : function(action){
28404         var o = action.options;
28405         
28406        
28407         if(this.waitMsgTarget === true){
28408             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28409         }else if(this.waitMsgTarget){
28410             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28411             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28412         }else {
28413             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28414         }
28415          
28416     },
28417
28418     // private
28419     afterAction : function(action, success){
28420         this.activeAction = null;
28421         var o = action.options;
28422         
28423         if(this.waitMsgTarget === true){
28424             this.el.unmask();
28425         }else if(this.waitMsgTarget){
28426             this.waitMsgTarget.unmask();
28427         }else{
28428             Roo.MessageBox.updateProgress(1);
28429             Roo.MessageBox.hide();
28430         }
28431          
28432         if(success){
28433             if(o.reset){
28434                 this.reset();
28435             }
28436             Roo.callback(o.success, o.scope, [this, action]);
28437             this.fireEvent('actioncomplete', this, action);
28438             
28439         }else{
28440             
28441             // failure condition..
28442             // we have a scenario where updates need confirming.
28443             // eg. if a locking scenario exists..
28444             // we look for { errors : { needs_confirm : true }} in the response.
28445             if (
28446                 (typeof(action.result) != 'undefined')  &&
28447                 (typeof(action.result.errors) != 'undefined')  &&
28448                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28449            ){
28450                 var _t = this;
28451                 Roo.MessageBox.confirm(
28452                     "Change requires confirmation",
28453                     action.result.errorMsg,
28454                     function(r) {
28455                         if (r != 'yes') {
28456                             return;
28457                         }
28458                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28459                     }
28460                     
28461                 );
28462                 
28463                 
28464                 
28465                 return;
28466             }
28467             
28468             Roo.callback(o.failure, o.scope, [this, action]);
28469             // show an error message if no failed handler is set..
28470             if (!this.hasListener('actionfailed')) {
28471                 Roo.MessageBox.alert("Error",
28472                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28473                         action.result.errorMsg :
28474                         "Saving Failed, please check your entries or try again"
28475                 );
28476             }
28477             
28478             this.fireEvent('actionfailed', this, action);
28479         }
28480         
28481     },
28482
28483     /**
28484      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28485      * @param {String} id The value to search for
28486      * @return Field
28487      */
28488     findField : function(id){
28489         var field = this.items.get(id);
28490         if(!field){
28491             this.items.each(function(f){
28492                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28493                     field = f;
28494                     return false;
28495                 }
28496             });
28497         }
28498         return field || null;
28499     },
28500
28501     /**
28502      * Add a secondary form to this one, 
28503      * Used to provide tabbed forms. One form is primary, with hidden values 
28504      * which mirror the elements from the other forms.
28505      * 
28506      * @param {Roo.form.Form} form to add.
28507      * 
28508      */
28509     addForm : function(form)
28510     {
28511        
28512         if (this.childForms.indexOf(form) > -1) {
28513             // already added..
28514             return;
28515         }
28516         this.childForms.push(form);
28517         var n = '';
28518         Roo.each(form.allItems, function (fe) {
28519             
28520             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28521             if (this.findField(n)) { // already added..
28522                 return;
28523             }
28524             var add = new Roo.form.Hidden({
28525                 name : n
28526             });
28527             add.render(this.el);
28528             
28529             this.add( add );
28530         }, this);
28531         
28532     },
28533     /**
28534      * Mark fields in this form invalid in bulk.
28535      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28536      * @return {BasicForm} this
28537      */
28538     markInvalid : function(errors){
28539         if(errors instanceof Array){
28540             for(var i = 0, len = errors.length; i < len; i++){
28541                 var fieldError = errors[i];
28542                 var f = this.findField(fieldError.id);
28543                 if(f){
28544                     f.markInvalid(fieldError.msg);
28545                 }
28546             }
28547         }else{
28548             var field, id;
28549             for(id in errors){
28550                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28551                     field.markInvalid(errors[id]);
28552                 }
28553             }
28554         }
28555         Roo.each(this.childForms || [], function (f) {
28556             f.markInvalid(errors);
28557         });
28558         
28559         return this;
28560     },
28561
28562     /**
28563      * Set values for fields in this form in bulk.
28564      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28565      * @return {BasicForm} this
28566      */
28567     setValues : function(values){
28568         if(values instanceof Array){ // array of objects
28569             for(var i = 0, len = values.length; i < len; i++){
28570                 var v = values[i];
28571                 var f = this.findField(v.id);
28572                 if(f){
28573                     f.setValue(v.value);
28574                     if(this.trackResetOnLoad){
28575                         f.originalValue = f.getValue();
28576                     }
28577                 }
28578             }
28579         }else{ // object hash
28580             var field, id;
28581             for(id in values){
28582                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28583                     
28584                     if (field.setFromData && 
28585                         field.valueField && 
28586                         field.displayField &&
28587                         // combos' with local stores can 
28588                         // be queried via setValue()
28589                         // to set their value..
28590                         (field.store && !field.store.isLocal)
28591                         ) {
28592                         // it's a combo
28593                         var sd = { };
28594                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28595                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28596                         field.setFromData(sd);
28597                         
28598                     } else {
28599                         field.setValue(values[id]);
28600                     }
28601                     
28602                     
28603                     if(this.trackResetOnLoad){
28604                         field.originalValue = field.getValue();
28605                     }
28606                 }
28607             }
28608         }
28609          
28610         Roo.each(this.childForms || [], function (f) {
28611             f.setValues(values);
28612         });
28613                 
28614         return this;
28615     },
28616
28617     /**
28618      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28619      * they are returned as an array.
28620      * @param {Boolean} asString
28621      * @return {Object}
28622      */
28623     getValues : function(asString){
28624         if (this.childForms) {
28625             // copy values from the child forms
28626             Roo.each(this.childForms, function (f) {
28627                 this.setValues(f.getValues());
28628             }, this);
28629         }
28630         
28631         
28632         
28633         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28634         if(asString === true){
28635             return fs;
28636         }
28637         return Roo.urlDecode(fs);
28638     },
28639     
28640     /**
28641      * Returns the fields in this form as an object with key/value pairs. 
28642      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28643      * @return {Object}
28644      */
28645     getFieldValues : function(with_hidden)
28646     {
28647         if (this.childForms) {
28648             // copy values from the child forms
28649             // should this call getFieldValues - probably not as we do not currently copy
28650             // hidden fields when we generate..
28651             Roo.each(this.childForms, function (f) {
28652                 this.setValues(f.getValues());
28653             }, this);
28654         }
28655         
28656         var ret = {};
28657         this.items.each(function(f){
28658             if (!f.getName()) {
28659                 return;
28660             }
28661             var v = f.getValue();
28662             if (f.inputType =='radio') {
28663                 if (typeof(ret[f.getName()]) == 'undefined') {
28664                     ret[f.getName()] = ''; // empty..
28665                 }
28666                 
28667                 if (!f.el.dom.checked) {
28668                     return;
28669                     
28670                 }
28671                 v = f.el.dom.value;
28672                 
28673             }
28674             
28675             // not sure if this supported any more..
28676             if ((typeof(v) == 'object') && f.getRawValue) {
28677                 v = f.getRawValue() ; // dates..
28678             }
28679             // combo boxes where name != hiddenName...
28680             if (f.name != f.getName()) {
28681                 ret[f.name] = f.getRawValue();
28682             }
28683             ret[f.getName()] = v;
28684         });
28685         
28686         return ret;
28687     },
28688
28689     /**
28690      * Clears all invalid messages in this form.
28691      * @return {BasicForm} this
28692      */
28693     clearInvalid : function(){
28694         this.items.each(function(f){
28695            f.clearInvalid();
28696         });
28697         
28698         Roo.each(this.childForms || [], function (f) {
28699             f.clearInvalid();
28700         });
28701         
28702         
28703         return this;
28704     },
28705
28706     /**
28707      * Resets this form.
28708      * @return {BasicForm} this
28709      */
28710     reset : function(){
28711         this.items.each(function(f){
28712             f.reset();
28713         });
28714         
28715         Roo.each(this.childForms || [], function (f) {
28716             f.reset();
28717         });
28718        
28719         
28720         return this;
28721     },
28722
28723     /**
28724      * Add Roo.form components to this form.
28725      * @param {Field} field1
28726      * @param {Field} field2 (optional)
28727      * @param {Field} etc (optional)
28728      * @return {BasicForm} this
28729      */
28730     add : function(){
28731         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28732         return this;
28733     },
28734
28735
28736     /**
28737      * Removes a field from the items collection (does NOT remove its markup).
28738      * @param {Field} field
28739      * @return {BasicForm} this
28740      */
28741     remove : function(field){
28742         this.items.remove(field);
28743         return this;
28744     },
28745
28746     /**
28747      * Looks at the fields in this form, checks them for an id attribute,
28748      * and calls applyTo on the existing dom element with that id.
28749      * @return {BasicForm} this
28750      */
28751     render : function(){
28752         this.items.each(function(f){
28753             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28754                 f.applyTo(f.id);
28755             }
28756         });
28757         return this;
28758     },
28759
28760     /**
28761      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28762      * @param {Object} values
28763      * @return {BasicForm} this
28764      */
28765     applyToFields : function(o){
28766         this.items.each(function(f){
28767            Roo.apply(f, o);
28768         });
28769         return this;
28770     },
28771
28772     /**
28773      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28774      * @param {Object} values
28775      * @return {BasicForm} this
28776      */
28777     applyIfToFields : function(o){
28778         this.items.each(function(f){
28779            Roo.applyIf(f, o);
28780         });
28781         return this;
28782     }
28783 });
28784
28785 // back compat
28786 Roo.BasicForm = Roo.form.BasicForm;/*
28787  * Based on:
28788  * Ext JS Library 1.1.1
28789  * Copyright(c) 2006-2007, Ext JS, LLC.
28790  *
28791  * Originally Released Under LGPL - original licence link has changed is not relivant.
28792  *
28793  * Fork - LGPL
28794  * <script type="text/javascript">
28795  */
28796
28797 /**
28798  * @class Roo.form.Form
28799  * @extends Roo.form.BasicForm
28800  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28801  * @constructor
28802  * @param {Object} config Configuration options
28803  */
28804 Roo.form.Form = function(config){
28805     var xitems =  [];
28806     if (config.items) {
28807         xitems = config.items;
28808         delete config.items;
28809     }
28810    
28811     
28812     Roo.form.Form.superclass.constructor.call(this, null, config);
28813     this.url = this.url || this.action;
28814     if(!this.root){
28815         this.root = new Roo.form.Layout(Roo.applyIf({
28816             id: Roo.id()
28817         }, config));
28818     }
28819     this.active = this.root;
28820     /**
28821      * Array of all the buttons that have been added to this form via {@link addButton}
28822      * @type Array
28823      */
28824     this.buttons = [];
28825     this.allItems = [];
28826     this.addEvents({
28827         /**
28828          * @event clientvalidation
28829          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28830          * @param {Form} this
28831          * @param {Boolean} valid true if the form has passed client-side validation
28832          */
28833         clientvalidation: true,
28834         /**
28835          * @event rendered
28836          * Fires when the form is rendered
28837          * @param {Roo.form.Form} form
28838          */
28839         rendered : true
28840     });
28841     
28842     if (this.progressUrl) {
28843             // push a hidden field onto the list of fields..
28844             this.addxtype( {
28845                     xns: Roo.form, 
28846                     xtype : 'Hidden', 
28847                     name : 'UPLOAD_IDENTIFIER' 
28848             });
28849         }
28850         
28851     
28852     Roo.each(xitems, this.addxtype, this);
28853     
28854     
28855     
28856 };
28857
28858 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28859     /**
28860      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28861      */
28862     /**
28863      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28864      */
28865     /**
28866      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28867      */
28868     buttonAlign:'center',
28869
28870     /**
28871      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28872      */
28873     minButtonWidth:75,
28874
28875     /**
28876      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28877      * This property cascades to child containers if not set.
28878      */
28879     labelAlign:'left',
28880
28881     /**
28882      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28883      * fires a looping event with that state. This is required to bind buttons to the valid
28884      * state using the config value formBind:true on the button.
28885      */
28886     monitorValid : false,
28887
28888     /**
28889      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28890      */
28891     monitorPoll : 200,
28892     
28893     /**
28894      * @cfg {String} progressUrl - Url to return progress data 
28895      */
28896     
28897     progressUrl : false,
28898   
28899     /**
28900      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28901      * fields are added and the column is closed. If no fields are passed the column remains open
28902      * until end() is called.
28903      * @param {Object} config The config to pass to the column
28904      * @param {Field} field1 (optional)
28905      * @param {Field} field2 (optional)
28906      * @param {Field} etc (optional)
28907      * @return Column The column container object
28908      */
28909     column : function(c){
28910         var col = new Roo.form.Column(c);
28911         this.start(col);
28912         if(arguments.length > 1){ // duplicate code required because of Opera
28913             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28914             this.end();
28915         }
28916         return col;
28917     },
28918
28919     /**
28920      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28921      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28922      * until end() is called.
28923      * @param {Object} config The config to pass to the fieldset
28924      * @param {Field} field1 (optional)
28925      * @param {Field} field2 (optional)
28926      * @param {Field} etc (optional)
28927      * @return FieldSet The fieldset container object
28928      */
28929     fieldset : function(c){
28930         var fs = new Roo.form.FieldSet(c);
28931         this.start(fs);
28932         if(arguments.length > 1){ // duplicate code required because of Opera
28933             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28934             this.end();
28935         }
28936         return fs;
28937     },
28938
28939     /**
28940      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28941      * fields are added and the container is closed. If no fields are passed the container remains open
28942      * until end() is called.
28943      * @param {Object} config The config to pass to the Layout
28944      * @param {Field} field1 (optional)
28945      * @param {Field} field2 (optional)
28946      * @param {Field} etc (optional)
28947      * @return Layout The container object
28948      */
28949     container : function(c){
28950         var l = new Roo.form.Layout(c);
28951         this.start(l);
28952         if(arguments.length > 1){ // duplicate code required because of Opera
28953             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28954             this.end();
28955         }
28956         return l;
28957     },
28958
28959     /**
28960      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28961      * @param {Object} container A Roo.form.Layout or subclass of Layout
28962      * @return {Form} this
28963      */
28964     start : function(c){
28965         // cascade label info
28966         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28967         this.active.stack.push(c);
28968         c.ownerCt = this.active;
28969         this.active = c;
28970         return this;
28971     },
28972
28973     /**
28974      * Closes the current open container
28975      * @return {Form} this
28976      */
28977     end : function(){
28978         if(this.active == this.root){
28979             return this;
28980         }
28981         this.active = this.active.ownerCt;
28982         return this;
28983     },
28984
28985     /**
28986      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28987      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28988      * as the label of the field.
28989      * @param {Field} field1
28990      * @param {Field} field2 (optional)
28991      * @param {Field} etc. (optional)
28992      * @return {Form} this
28993      */
28994     add : function(){
28995         this.active.stack.push.apply(this.active.stack, arguments);
28996         this.allItems.push.apply(this.allItems,arguments);
28997         var r = [];
28998         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28999             if(a[i].isFormField){
29000                 r.push(a[i]);
29001             }
29002         }
29003         if(r.length > 0){
29004             Roo.form.Form.superclass.add.apply(this, r);
29005         }
29006         return this;
29007     },
29008     
29009
29010     
29011     
29012     
29013      /**
29014      * Find any element that has been added to a form, using it's ID or name
29015      * This can include framesets, columns etc. along with regular fields..
29016      * @param {String} id - id or name to find.
29017      
29018      * @return {Element} e - or false if nothing found.
29019      */
29020     findbyId : function(id)
29021     {
29022         var ret = false;
29023         if (!id) {
29024             return ret;
29025         }
29026         Roo.each(this.allItems, function(f){
29027             if (f.id == id || f.name == id ){
29028                 ret = f;
29029                 return false;
29030             }
29031         });
29032         return ret;
29033     },
29034
29035     
29036     
29037     /**
29038      * Render this form into the passed container. This should only be called once!
29039      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29040      * @return {Form} this
29041      */
29042     render : function(ct)
29043     {
29044         
29045         
29046         
29047         ct = Roo.get(ct);
29048         var o = this.autoCreate || {
29049             tag: 'form',
29050             method : this.method || 'POST',
29051             id : this.id || Roo.id()
29052         };
29053         this.initEl(ct.createChild(o));
29054
29055         this.root.render(this.el);
29056         
29057        
29058              
29059         this.items.each(function(f){
29060             f.render('x-form-el-'+f.id);
29061         });
29062
29063         if(this.buttons.length > 0){
29064             // tables are required to maintain order and for correct IE layout
29065             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29066                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29067                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29068             }}, null, true);
29069             var tr = tb.getElementsByTagName('tr')[0];
29070             for(var i = 0, len = this.buttons.length; i < len; i++) {
29071                 var b = this.buttons[i];
29072                 var td = document.createElement('td');
29073                 td.className = 'x-form-btn-td';
29074                 b.render(tr.appendChild(td));
29075             }
29076         }
29077         if(this.monitorValid){ // initialize after render
29078             this.startMonitoring();
29079         }
29080         this.fireEvent('rendered', this);
29081         return this;
29082     },
29083
29084     /**
29085      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29086      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29087      * object or a valid Roo.DomHelper element config
29088      * @param {Function} handler The function called when the button is clicked
29089      * @param {Object} scope (optional) The scope of the handler function
29090      * @return {Roo.Button}
29091      */
29092     addButton : function(config, handler, scope){
29093         var bc = {
29094             handler: handler,
29095             scope: scope,
29096             minWidth: this.minButtonWidth,
29097             hideParent:true
29098         };
29099         if(typeof config == "string"){
29100             bc.text = config;
29101         }else{
29102             Roo.apply(bc, config);
29103         }
29104         var btn = new Roo.Button(null, bc);
29105         this.buttons.push(btn);
29106         return btn;
29107     },
29108
29109      /**
29110      * Adds a series of form elements (using the xtype property as the factory method.
29111      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29112      * @param {Object} config 
29113      */
29114     
29115     addxtype : function()
29116     {
29117         var ar = Array.prototype.slice.call(arguments, 0);
29118         var ret = false;
29119         for(var i = 0; i < ar.length; i++) {
29120             if (!ar[i]) {
29121                 continue; // skip -- if this happends something invalid got sent, we 
29122                 // should ignore it, as basically that interface element will not show up
29123                 // and that should be pretty obvious!!
29124             }
29125             
29126             if (Roo.form[ar[i].xtype]) {
29127                 ar[i].form = this;
29128                 var fe = Roo.factory(ar[i], Roo.form);
29129                 if (!ret) {
29130                     ret = fe;
29131                 }
29132                 fe.form = this;
29133                 if (fe.store) {
29134                     fe.store.form = this;
29135                 }
29136                 if (fe.isLayout) {  
29137                          
29138                     this.start(fe);
29139                     this.allItems.push(fe);
29140                     if (fe.items && fe.addxtype) {
29141                         fe.addxtype.apply(fe, fe.items);
29142                         delete fe.items;
29143                     }
29144                      this.end();
29145                     continue;
29146                 }
29147                 
29148                 
29149                  
29150                 this.add(fe);
29151               //  console.log('adding ' + ar[i].xtype);
29152             }
29153             if (ar[i].xtype == 'Button') {  
29154                 //console.log('adding button');
29155                 //console.log(ar[i]);
29156                 this.addButton(ar[i]);
29157                 this.allItems.push(fe);
29158                 continue;
29159             }
29160             
29161             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29162                 alert('end is not supported on xtype any more, use items');
29163             //    this.end();
29164             //    //console.log('adding end');
29165             }
29166             
29167         }
29168         return ret;
29169     },
29170     
29171     /**
29172      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29173      * option "monitorValid"
29174      */
29175     startMonitoring : function(){
29176         if(!this.bound){
29177             this.bound = true;
29178             Roo.TaskMgr.start({
29179                 run : this.bindHandler,
29180                 interval : this.monitorPoll || 200,
29181                 scope: this
29182             });
29183         }
29184     },
29185
29186     /**
29187      * Stops monitoring of the valid state of this form
29188      */
29189     stopMonitoring : function(){
29190         this.bound = false;
29191     },
29192
29193     // private
29194     bindHandler : function(){
29195         if(!this.bound){
29196             return false; // stops binding
29197         }
29198         var valid = true;
29199         this.items.each(function(f){
29200             if(!f.isValid(true)){
29201                 valid = false;
29202                 return false;
29203             }
29204         });
29205         for(var i = 0, len = this.buttons.length; i < len; i++){
29206             var btn = this.buttons[i];
29207             if(btn.formBind === true && btn.disabled === valid){
29208                 btn.setDisabled(!valid);
29209             }
29210         }
29211         this.fireEvent('clientvalidation', this, valid);
29212     }
29213     
29214     
29215     
29216     
29217     
29218     
29219     
29220     
29221 });
29222
29223
29224 // back compat
29225 Roo.Form = Roo.form.Form;
29226 /*
29227  * Based on:
29228  * Ext JS Library 1.1.1
29229  * Copyright(c) 2006-2007, Ext JS, LLC.
29230  *
29231  * Originally Released Under LGPL - original licence link has changed is not relivant.
29232  *
29233  * Fork - LGPL
29234  * <script type="text/javascript">
29235  */
29236
29237 // as we use this in bootstrap.
29238 Roo.namespace('Roo.form');
29239  /**
29240  * @class Roo.form.Action
29241  * Internal Class used to handle form actions
29242  * @constructor
29243  * @param {Roo.form.BasicForm} el The form element or its id
29244  * @param {Object} config Configuration options
29245  */
29246
29247  
29248  
29249 // define the action interface
29250 Roo.form.Action = function(form, options){
29251     this.form = form;
29252     this.options = options || {};
29253 };
29254 /**
29255  * Client Validation Failed
29256  * @const 
29257  */
29258 Roo.form.Action.CLIENT_INVALID = 'client';
29259 /**
29260  * Server Validation Failed
29261  * @const 
29262  */
29263 Roo.form.Action.SERVER_INVALID = 'server';
29264  /**
29265  * Connect to Server Failed
29266  * @const 
29267  */
29268 Roo.form.Action.CONNECT_FAILURE = 'connect';
29269 /**
29270  * Reading Data from Server Failed
29271  * @const 
29272  */
29273 Roo.form.Action.LOAD_FAILURE = 'load';
29274
29275 Roo.form.Action.prototype = {
29276     type : 'default',
29277     failureType : undefined,
29278     response : undefined,
29279     result : undefined,
29280
29281     // interface method
29282     run : function(options){
29283
29284     },
29285
29286     // interface method
29287     success : function(response){
29288
29289     },
29290
29291     // interface method
29292     handleResponse : function(response){
29293
29294     },
29295
29296     // default connection failure
29297     failure : function(response){
29298         
29299         this.response = response;
29300         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29301         this.form.afterAction(this, false);
29302     },
29303
29304     processResponse : function(response){
29305         this.response = response;
29306         if(!response.responseText){
29307             return true;
29308         }
29309         this.result = this.handleResponse(response);
29310         return this.result;
29311     },
29312
29313     // utility functions used internally
29314     getUrl : function(appendParams){
29315         var url = this.options.url || this.form.url || this.form.el.dom.action;
29316         if(appendParams){
29317             var p = this.getParams();
29318             if(p){
29319                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29320             }
29321         }
29322         return url;
29323     },
29324
29325     getMethod : function(){
29326         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29327     },
29328
29329     getParams : function(){
29330         var bp = this.form.baseParams;
29331         var p = this.options.params;
29332         if(p){
29333             if(typeof p == "object"){
29334                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29335             }else if(typeof p == 'string' && bp){
29336                 p += '&' + Roo.urlEncode(bp);
29337             }
29338         }else if(bp){
29339             p = Roo.urlEncode(bp);
29340         }
29341         return p;
29342     },
29343
29344     createCallback : function(){
29345         return {
29346             success: this.success,
29347             failure: this.failure,
29348             scope: this,
29349             timeout: (this.form.timeout*1000),
29350             upload: this.form.fileUpload ? this.success : undefined
29351         };
29352     }
29353 };
29354
29355 Roo.form.Action.Submit = function(form, options){
29356     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29357 };
29358
29359 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29360     type : 'submit',
29361
29362     haveProgress : false,
29363     uploadComplete : false,
29364     
29365     // uploadProgress indicator.
29366     uploadProgress : function()
29367     {
29368         if (!this.form.progressUrl) {
29369             return;
29370         }
29371         
29372         if (!this.haveProgress) {
29373             Roo.MessageBox.progress("Uploading", "Uploading");
29374         }
29375         if (this.uploadComplete) {
29376            Roo.MessageBox.hide();
29377            return;
29378         }
29379         
29380         this.haveProgress = true;
29381    
29382         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29383         
29384         var c = new Roo.data.Connection();
29385         c.request({
29386             url : this.form.progressUrl,
29387             params: {
29388                 id : uid
29389             },
29390             method: 'GET',
29391             success : function(req){
29392                //console.log(data);
29393                 var rdata = false;
29394                 var edata;
29395                 try  {
29396                    rdata = Roo.decode(req.responseText)
29397                 } catch (e) {
29398                     Roo.log("Invalid data from server..");
29399                     Roo.log(edata);
29400                     return;
29401                 }
29402                 if (!rdata || !rdata.success) {
29403                     Roo.log(rdata);
29404                     Roo.MessageBox.alert(Roo.encode(rdata));
29405                     return;
29406                 }
29407                 var data = rdata.data;
29408                 
29409                 if (this.uploadComplete) {
29410                    Roo.MessageBox.hide();
29411                    return;
29412                 }
29413                    
29414                 if (data){
29415                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29416                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29417                     );
29418                 }
29419                 this.uploadProgress.defer(2000,this);
29420             },
29421        
29422             failure: function(data) {
29423                 Roo.log('progress url failed ');
29424                 Roo.log(data);
29425             },
29426             scope : this
29427         });
29428            
29429     },
29430     
29431     
29432     run : function()
29433     {
29434         // run get Values on the form, so it syncs any secondary forms.
29435         this.form.getValues();
29436         
29437         var o = this.options;
29438         var method = this.getMethod();
29439         var isPost = method == 'POST';
29440         if(o.clientValidation === false || this.form.isValid()){
29441             
29442             if (this.form.progressUrl) {
29443                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29444                     (new Date() * 1) + '' + Math.random());
29445                     
29446             } 
29447             
29448             
29449             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29450                 form:this.form.el.dom,
29451                 url:this.getUrl(!isPost),
29452                 method: method,
29453                 params:isPost ? this.getParams() : null,
29454                 isUpload: this.form.fileUpload
29455             }));
29456             
29457             this.uploadProgress();
29458
29459         }else if (o.clientValidation !== false){ // client validation failed
29460             this.failureType = Roo.form.Action.CLIENT_INVALID;
29461             this.form.afterAction(this, false);
29462         }
29463     },
29464
29465     success : function(response)
29466     {
29467         this.uploadComplete= true;
29468         if (this.haveProgress) {
29469             Roo.MessageBox.hide();
29470         }
29471         
29472         
29473         var result = this.processResponse(response);
29474         if(result === true || result.success){
29475             this.form.afterAction(this, true);
29476             return;
29477         }
29478         if(result.errors){
29479             this.form.markInvalid(result.errors);
29480             this.failureType = Roo.form.Action.SERVER_INVALID;
29481         }
29482         this.form.afterAction(this, false);
29483     },
29484     failure : function(response)
29485     {
29486         this.uploadComplete= true;
29487         if (this.haveProgress) {
29488             Roo.MessageBox.hide();
29489         }
29490         
29491         this.response = response;
29492         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29493         this.form.afterAction(this, false);
29494     },
29495     
29496     handleResponse : function(response){
29497         if(this.form.errorReader){
29498             var rs = this.form.errorReader.read(response);
29499             var errors = [];
29500             if(rs.records){
29501                 for(var i = 0, len = rs.records.length; i < len; i++) {
29502                     var r = rs.records[i];
29503                     errors[i] = r.data;
29504                 }
29505             }
29506             if(errors.length < 1){
29507                 errors = null;
29508             }
29509             return {
29510                 success : rs.success,
29511                 errors : errors
29512             };
29513         }
29514         var ret = false;
29515         try {
29516             ret = Roo.decode(response.responseText);
29517         } catch (e) {
29518             ret = {
29519                 success: false,
29520                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29521                 errors : []
29522             };
29523         }
29524         return ret;
29525         
29526     }
29527 });
29528
29529
29530 Roo.form.Action.Load = function(form, options){
29531     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29532     this.reader = this.form.reader;
29533 };
29534
29535 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29536     type : 'load',
29537
29538     run : function(){
29539         
29540         Roo.Ajax.request(Roo.apply(
29541                 this.createCallback(), {
29542                     method:this.getMethod(),
29543                     url:this.getUrl(false),
29544                     params:this.getParams()
29545         }));
29546     },
29547
29548     success : function(response){
29549         
29550         var result = this.processResponse(response);
29551         if(result === true || !result.success || !result.data){
29552             this.failureType = Roo.form.Action.LOAD_FAILURE;
29553             this.form.afterAction(this, false);
29554             return;
29555         }
29556         this.form.clearInvalid();
29557         this.form.setValues(result.data);
29558         this.form.afterAction(this, true);
29559     },
29560
29561     handleResponse : function(response){
29562         if(this.form.reader){
29563             var rs = this.form.reader.read(response);
29564             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29565             return {
29566                 success : rs.success,
29567                 data : data
29568             };
29569         }
29570         return Roo.decode(response.responseText);
29571     }
29572 });
29573
29574 Roo.form.Action.ACTION_TYPES = {
29575     'load' : Roo.form.Action.Load,
29576     'submit' : Roo.form.Action.Submit
29577 };/*
29578  * Based on:
29579  * Ext JS Library 1.1.1
29580  * Copyright(c) 2006-2007, Ext JS, LLC.
29581  *
29582  * Originally Released Under LGPL - original licence link has changed is not relivant.
29583  *
29584  * Fork - LGPL
29585  * <script type="text/javascript">
29586  */
29587  
29588 /**
29589  * @class Roo.form.Layout
29590  * @extends Roo.Component
29591  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29592  * @constructor
29593  * @param {Object} config Configuration options
29594  */
29595 Roo.form.Layout = function(config){
29596     var xitems = [];
29597     if (config.items) {
29598         xitems = config.items;
29599         delete config.items;
29600     }
29601     Roo.form.Layout.superclass.constructor.call(this, config);
29602     this.stack = [];
29603     Roo.each(xitems, this.addxtype, this);
29604      
29605 };
29606
29607 Roo.extend(Roo.form.Layout, Roo.Component, {
29608     /**
29609      * @cfg {String/Object} autoCreate
29610      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29611      */
29612     /**
29613      * @cfg {String/Object/Function} style
29614      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29615      * a function which returns such a specification.
29616      */
29617     /**
29618      * @cfg {String} labelAlign
29619      * Valid values are "left," "top" and "right" (defaults to "left")
29620      */
29621     /**
29622      * @cfg {Number} labelWidth
29623      * Fixed width in pixels of all field labels (defaults to undefined)
29624      */
29625     /**
29626      * @cfg {Boolean} clear
29627      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29628      */
29629     clear : true,
29630     /**
29631      * @cfg {String} labelSeparator
29632      * The separator to use after field labels (defaults to ':')
29633      */
29634     labelSeparator : ':',
29635     /**
29636      * @cfg {Boolean} hideLabels
29637      * True to suppress the display of field labels in this layout (defaults to false)
29638      */
29639     hideLabels : false,
29640
29641     // private
29642     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29643     
29644     isLayout : true,
29645     
29646     // private
29647     onRender : function(ct, position){
29648         if(this.el){ // from markup
29649             this.el = Roo.get(this.el);
29650         }else {  // generate
29651             var cfg = this.getAutoCreate();
29652             this.el = ct.createChild(cfg, position);
29653         }
29654         if(this.style){
29655             this.el.applyStyles(this.style);
29656         }
29657         if(this.labelAlign){
29658             this.el.addClass('x-form-label-'+this.labelAlign);
29659         }
29660         if(this.hideLabels){
29661             this.labelStyle = "display:none";
29662             this.elementStyle = "padding-left:0;";
29663         }else{
29664             if(typeof this.labelWidth == 'number'){
29665                 this.labelStyle = "width:"+this.labelWidth+"px;";
29666                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29667             }
29668             if(this.labelAlign == 'top'){
29669                 this.labelStyle = "width:auto;";
29670                 this.elementStyle = "padding-left:0;";
29671             }
29672         }
29673         var stack = this.stack;
29674         var slen = stack.length;
29675         if(slen > 0){
29676             if(!this.fieldTpl){
29677                 var t = new Roo.Template(
29678                     '<div class="x-form-item {5}">',
29679                         '<label for="{0}" style="{2}">{1}{4}</label>',
29680                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29681                         '</div>',
29682                     '</div><div class="x-form-clear-left"></div>'
29683                 );
29684                 t.disableFormats = true;
29685                 t.compile();
29686                 Roo.form.Layout.prototype.fieldTpl = t;
29687             }
29688             for(var i = 0; i < slen; i++) {
29689                 if(stack[i].isFormField){
29690                     this.renderField(stack[i]);
29691                 }else{
29692                     this.renderComponent(stack[i]);
29693                 }
29694             }
29695         }
29696         if(this.clear){
29697             this.el.createChild({cls:'x-form-clear'});
29698         }
29699     },
29700
29701     // private
29702     renderField : function(f){
29703         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29704                f.id, //0
29705                f.fieldLabel, //1
29706                f.labelStyle||this.labelStyle||'', //2
29707                this.elementStyle||'', //3
29708                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29709                f.itemCls||this.itemCls||''  //5
29710        ], true).getPrevSibling());
29711     },
29712
29713     // private
29714     renderComponent : function(c){
29715         c.render(c.isLayout ? this.el : this.el.createChild());    
29716     },
29717     /**
29718      * Adds a object form elements (using the xtype property as the factory method.)
29719      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29720      * @param {Object} config 
29721      */
29722     addxtype : function(o)
29723     {
29724         // create the lement.
29725         o.form = this.form;
29726         var fe = Roo.factory(o, Roo.form);
29727         this.form.allItems.push(fe);
29728         this.stack.push(fe);
29729         
29730         if (fe.isFormField) {
29731             this.form.items.add(fe);
29732         }
29733          
29734         return fe;
29735     }
29736 });
29737
29738 /**
29739  * @class Roo.form.Column
29740  * @extends Roo.form.Layout
29741  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29742  * @constructor
29743  * @param {Object} config Configuration options
29744  */
29745 Roo.form.Column = function(config){
29746     Roo.form.Column.superclass.constructor.call(this, config);
29747 };
29748
29749 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29750     /**
29751      * @cfg {Number/String} width
29752      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29753      */
29754     /**
29755      * @cfg {String/Object} autoCreate
29756      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29757      */
29758
29759     // private
29760     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29761
29762     // private
29763     onRender : function(ct, position){
29764         Roo.form.Column.superclass.onRender.call(this, ct, position);
29765         if(this.width){
29766             this.el.setWidth(this.width);
29767         }
29768     }
29769 });
29770
29771
29772 /**
29773  * @class Roo.form.Row
29774  * @extends Roo.form.Layout
29775  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29776  * @constructor
29777  * @param {Object} config Configuration options
29778  */
29779
29780  
29781 Roo.form.Row = function(config){
29782     Roo.form.Row.superclass.constructor.call(this, config);
29783 };
29784  
29785 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29786       /**
29787      * @cfg {Number/String} width
29788      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29789      */
29790     /**
29791      * @cfg {Number/String} height
29792      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29793      */
29794     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29795     
29796     padWidth : 20,
29797     // private
29798     onRender : function(ct, position){
29799         //console.log('row render');
29800         if(!this.rowTpl){
29801             var t = new Roo.Template(
29802                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29803                     '<label for="{0}" style="{2}">{1}{4}</label>',
29804                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29805                     '</div>',
29806                 '</div>'
29807             );
29808             t.disableFormats = true;
29809             t.compile();
29810             Roo.form.Layout.prototype.rowTpl = t;
29811         }
29812         this.fieldTpl = this.rowTpl;
29813         
29814         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29815         var labelWidth = 100;
29816         
29817         if ((this.labelAlign != 'top')) {
29818             if (typeof this.labelWidth == 'number') {
29819                 labelWidth = this.labelWidth
29820             }
29821             this.padWidth =  20 + labelWidth;
29822             
29823         }
29824         
29825         Roo.form.Column.superclass.onRender.call(this, ct, position);
29826         if(this.width){
29827             this.el.setWidth(this.width);
29828         }
29829         if(this.height){
29830             this.el.setHeight(this.height);
29831         }
29832     },
29833     
29834     // private
29835     renderField : function(f){
29836         f.fieldEl = this.fieldTpl.append(this.el, [
29837                f.id, f.fieldLabel,
29838                f.labelStyle||this.labelStyle||'',
29839                this.elementStyle||'',
29840                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29841                f.itemCls||this.itemCls||'',
29842                f.width ? f.width + this.padWidth : 160 + this.padWidth
29843        ],true);
29844     }
29845 });
29846  
29847
29848 /**
29849  * @class Roo.form.FieldSet
29850  * @extends Roo.form.Layout
29851  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29852  * @constructor
29853  * @param {Object} config Configuration options
29854  */
29855 Roo.form.FieldSet = function(config){
29856     Roo.form.FieldSet.superclass.constructor.call(this, config);
29857 };
29858
29859 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29860     /**
29861      * @cfg {String} legend
29862      * The text to display as the legend for the FieldSet (defaults to '')
29863      */
29864     /**
29865      * @cfg {String/Object} autoCreate
29866      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29867      */
29868
29869     // private
29870     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29871
29872     // private
29873     onRender : function(ct, position){
29874         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29875         if(this.legend){
29876             this.setLegend(this.legend);
29877         }
29878     },
29879
29880     // private
29881     setLegend : function(text){
29882         if(this.rendered){
29883             this.el.child('legend').update(text);
29884         }
29885     }
29886 });/*
29887  * Based on:
29888  * Ext JS Library 1.1.1
29889  * Copyright(c) 2006-2007, Ext JS, LLC.
29890  *
29891  * Originally Released Under LGPL - original licence link has changed is not relivant.
29892  *
29893  * Fork - LGPL
29894  * <script type="text/javascript">
29895  */
29896 /**
29897  * @class Roo.form.VTypes
29898  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29899  * @singleton
29900  */
29901 Roo.form.VTypes = function(){
29902     // closure these in so they are only created once.
29903     var alpha = /^[a-zA-Z_]+$/;
29904     var alphanum = /^[a-zA-Z0-9_]+$/;
29905     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29906     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29907
29908     // All these messages and functions are configurable
29909     return {
29910         /**
29911          * The function used to validate email addresses
29912          * @param {String} value The email address
29913          */
29914         'email' : function(v){
29915             return email.test(v);
29916         },
29917         /**
29918          * The error text to display when the email validation function returns false
29919          * @type String
29920          */
29921         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29922         /**
29923          * The keystroke filter mask to be applied on email input
29924          * @type RegExp
29925          */
29926         'emailMask' : /[a-z0-9_\.\-@]/i,
29927
29928         /**
29929          * The function used to validate URLs
29930          * @param {String} value The URL
29931          */
29932         'url' : function(v){
29933             return url.test(v);
29934         },
29935         /**
29936          * The error text to display when the url validation function returns false
29937          * @type String
29938          */
29939         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29940         
29941         /**
29942          * The function used to validate alpha values
29943          * @param {String} value The value
29944          */
29945         'alpha' : function(v){
29946             return alpha.test(v);
29947         },
29948         /**
29949          * The error text to display when the alpha validation function returns false
29950          * @type String
29951          */
29952         'alphaText' : 'This field should only contain letters and _',
29953         /**
29954          * The keystroke filter mask to be applied on alpha input
29955          * @type RegExp
29956          */
29957         'alphaMask' : /[a-z_]/i,
29958
29959         /**
29960          * The function used to validate alphanumeric values
29961          * @param {String} value The value
29962          */
29963         'alphanum' : function(v){
29964             return alphanum.test(v);
29965         },
29966         /**
29967          * The error text to display when the alphanumeric validation function returns false
29968          * @type String
29969          */
29970         'alphanumText' : 'This field should only contain letters, numbers and _',
29971         /**
29972          * The keystroke filter mask to be applied on alphanumeric input
29973          * @type RegExp
29974          */
29975         'alphanumMask' : /[a-z0-9_]/i
29976     };
29977 }();//<script type="text/javascript">
29978
29979 /**
29980  * @class Roo.form.FCKeditor
29981  * @extends Roo.form.TextArea
29982  * Wrapper around the FCKEditor http://www.fckeditor.net
29983  * @constructor
29984  * Creates a new FCKeditor
29985  * @param {Object} config Configuration options
29986  */
29987 Roo.form.FCKeditor = function(config){
29988     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29989     this.addEvents({
29990          /**
29991          * @event editorinit
29992          * Fired when the editor is initialized - you can add extra handlers here..
29993          * @param {FCKeditor} this
29994          * @param {Object} the FCK object.
29995          */
29996         editorinit : true
29997     });
29998     
29999     
30000 };
30001 Roo.form.FCKeditor.editors = { };
30002 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30003 {
30004     //defaultAutoCreate : {
30005     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30006     //},
30007     // private
30008     /**
30009      * @cfg {Object} fck options - see fck manual for details.
30010      */
30011     fckconfig : false,
30012     
30013     /**
30014      * @cfg {Object} fck toolbar set (Basic or Default)
30015      */
30016     toolbarSet : 'Basic',
30017     /**
30018      * @cfg {Object} fck BasePath
30019      */ 
30020     basePath : '/fckeditor/',
30021     
30022     
30023     frame : false,
30024     
30025     value : '',
30026     
30027    
30028     onRender : function(ct, position)
30029     {
30030         if(!this.el){
30031             this.defaultAutoCreate = {
30032                 tag: "textarea",
30033                 style:"width:300px;height:60px;",
30034                 autocomplete: "off"
30035             };
30036         }
30037         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30038         /*
30039         if(this.grow){
30040             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30041             if(this.preventScrollbars){
30042                 this.el.setStyle("overflow", "hidden");
30043             }
30044             this.el.setHeight(this.growMin);
30045         }
30046         */
30047         //console.log('onrender' + this.getId() );
30048         Roo.form.FCKeditor.editors[this.getId()] = this;
30049          
30050
30051         this.replaceTextarea() ;
30052         
30053     },
30054     
30055     getEditor : function() {
30056         return this.fckEditor;
30057     },
30058     /**
30059      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30060      * @param {Mixed} value The value to set
30061      */
30062     
30063     
30064     setValue : function(value)
30065     {
30066         //console.log('setValue: ' + value);
30067         
30068         if(typeof(value) == 'undefined') { // not sure why this is happending...
30069             return;
30070         }
30071         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30072         
30073         //if(!this.el || !this.getEditor()) {
30074         //    this.value = value;
30075             //this.setValue.defer(100,this,[value]);    
30076         //    return;
30077         //} 
30078         
30079         if(!this.getEditor()) {
30080             return;
30081         }
30082         
30083         this.getEditor().SetData(value);
30084         
30085         //
30086
30087     },
30088
30089     /**
30090      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30091      * @return {Mixed} value The field value
30092      */
30093     getValue : function()
30094     {
30095         
30096         if (this.frame && this.frame.dom.style.display == 'none') {
30097             return Roo.form.FCKeditor.superclass.getValue.call(this);
30098         }
30099         
30100         if(!this.el || !this.getEditor()) {
30101            
30102            // this.getValue.defer(100,this); 
30103             return this.value;
30104         }
30105        
30106         
30107         var value=this.getEditor().GetData();
30108         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30109         return Roo.form.FCKeditor.superclass.getValue.call(this);
30110         
30111
30112     },
30113
30114     /**
30115      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30116      * @return {Mixed} value The field value
30117      */
30118     getRawValue : function()
30119     {
30120         if (this.frame && this.frame.dom.style.display == 'none') {
30121             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30122         }
30123         
30124         if(!this.el || !this.getEditor()) {
30125             //this.getRawValue.defer(100,this); 
30126             return this.value;
30127             return;
30128         }
30129         
30130         
30131         
30132         var value=this.getEditor().GetData();
30133         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30134         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30135          
30136     },
30137     
30138     setSize : function(w,h) {
30139         
30140         
30141         
30142         //if (this.frame && this.frame.dom.style.display == 'none') {
30143         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30144         //    return;
30145         //}
30146         //if(!this.el || !this.getEditor()) {
30147         //    this.setSize.defer(100,this, [w,h]); 
30148         //    return;
30149         //}
30150         
30151         
30152         
30153         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30154         
30155         this.frame.dom.setAttribute('width', w);
30156         this.frame.dom.setAttribute('height', h);
30157         this.frame.setSize(w,h);
30158         
30159     },
30160     
30161     toggleSourceEdit : function(value) {
30162         
30163       
30164          
30165         this.el.dom.style.display = value ? '' : 'none';
30166         this.frame.dom.style.display = value ?  'none' : '';
30167         
30168     },
30169     
30170     
30171     focus: function(tag)
30172     {
30173         if (this.frame.dom.style.display == 'none') {
30174             return Roo.form.FCKeditor.superclass.focus.call(this);
30175         }
30176         if(!this.el || !this.getEditor()) {
30177             this.focus.defer(100,this, [tag]); 
30178             return;
30179         }
30180         
30181         
30182         
30183         
30184         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30185         this.getEditor().Focus();
30186         if (tgs.length) {
30187             if (!this.getEditor().Selection.GetSelection()) {
30188                 this.focus.defer(100,this, [tag]); 
30189                 return;
30190             }
30191             
30192             
30193             var r = this.getEditor().EditorDocument.createRange();
30194             r.setStart(tgs[0],0);
30195             r.setEnd(tgs[0],0);
30196             this.getEditor().Selection.GetSelection().removeAllRanges();
30197             this.getEditor().Selection.GetSelection().addRange(r);
30198             this.getEditor().Focus();
30199         }
30200         
30201     },
30202     
30203     
30204     
30205     replaceTextarea : function()
30206     {
30207         if ( document.getElementById( this.getId() + '___Frame' ) )
30208             return ;
30209         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30210         //{
30211             // We must check the elements firstly using the Id and then the name.
30212         var oTextarea = document.getElementById( this.getId() );
30213         
30214         var colElementsByName = document.getElementsByName( this.getId() ) ;
30215          
30216         oTextarea.style.display = 'none' ;
30217
30218         if ( oTextarea.tabIndex ) {            
30219             this.TabIndex = oTextarea.tabIndex ;
30220         }
30221         
30222         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30223         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30224         this.frame = Roo.get(this.getId() + '___Frame')
30225     },
30226     
30227     _getConfigHtml : function()
30228     {
30229         var sConfig = '' ;
30230
30231         for ( var o in this.fckconfig ) {
30232             sConfig += sConfig.length > 0  ? '&amp;' : '';
30233             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30234         }
30235
30236         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30237     },
30238     
30239     
30240     _getIFrameHtml : function()
30241     {
30242         var sFile = 'fckeditor.html' ;
30243         /* no idea what this is about..
30244         try
30245         {
30246             if ( (/fcksource=true/i).test( window.top.location.search ) )
30247                 sFile = 'fckeditor.original.html' ;
30248         }
30249         catch (e) { 
30250         */
30251
30252         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30253         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30254         
30255         
30256         var html = '<iframe id="' + this.getId() +
30257             '___Frame" src="' + sLink +
30258             '" width="' + this.width +
30259             '" height="' + this.height + '"' +
30260             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30261             ' frameborder="0" scrolling="no"></iframe>' ;
30262
30263         return html ;
30264     },
30265     
30266     _insertHtmlBefore : function( html, element )
30267     {
30268         if ( element.insertAdjacentHTML )       {
30269             // IE
30270             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30271         } else { // Gecko
30272             var oRange = document.createRange() ;
30273             oRange.setStartBefore( element ) ;
30274             var oFragment = oRange.createContextualFragment( html );
30275             element.parentNode.insertBefore( oFragment, element ) ;
30276         }
30277     }
30278     
30279     
30280   
30281     
30282     
30283     
30284     
30285
30286 });
30287
30288 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30289
30290 function FCKeditor_OnComplete(editorInstance){
30291     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30292     f.fckEditor = editorInstance;
30293     //console.log("loaded");
30294     f.fireEvent('editorinit', f, editorInstance);
30295
30296   
30297
30298  
30299
30300
30301
30302
30303
30304
30305
30306
30307
30308
30309
30310
30311
30312
30313
30314 //<script type="text/javascript">
30315 /**
30316  * @class Roo.form.GridField
30317  * @extends Roo.form.Field
30318  * Embed a grid (or editable grid into a form)
30319  * STATUS ALPHA
30320  * 
30321  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30322  * it needs 
30323  * xgrid.store = Roo.data.Store
30324  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30325  * xgrid.store.reader = Roo.data.JsonReader 
30326  * 
30327  * 
30328  * @constructor
30329  * Creates a new GridField
30330  * @param {Object} config Configuration options
30331  */
30332 Roo.form.GridField = function(config){
30333     Roo.form.GridField.superclass.constructor.call(this, config);
30334      
30335 };
30336
30337 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30338     /**
30339      * @cfg {Number} width  - used to restrict width of grid..
30340      */
30341     width : 100,
30342     /**
30343      * @cfg {Number} height - used to restrict height of grid..
30344      */
30345     height : 50,
30346      /**
30347      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30348          * 
30349          *}
30350      */
30351     xgrid : false, 
30352     /**
30353      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30354      * {tag: "input", type: "checkbox", autocomplete: "off"})
30355      */
30356    // defaultAutoCreate : { tag: 'div' },
30357     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30358     /**
30359      * @cfg {String} addTitle Text to include for adding a title.
30360      */
30361     addTitle : false,
30362     //
30363     onResize : function(){
30364         Roo.form.Field.superclass.onResize.apply(this, arguments);
30365     },
30366
30367     initEvents : function(){
30368         // Roo.form.Checkbox.superclass.initEvents.call(this);
30369         // has no events...
30370        
30371     },
30372
30373
30374     getResizeEl : function(){
30375         return this.wrap;
30376     },
30377
30378     getPositionEl : function(){
30379         return this.wrap;
30380     },
30381
30382     // private
30383     onRender : function(ct, position){
30384         
30385         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30386         var style = this.style;
30387         delete this.style;
30388         
30389         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30390         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30391         this.viewEl = this.wrap.createChild({ tag: 'div' });
30392         if (style) {
30393             this.viewEl.applyStyles(style);
30394         }
30395         if (this.width) {
30396             this.viewEl.setWidth(this.width);
30397         }
30398         if (this.height) {
30399             this.viewEl.setHeight(this.height);
30400         }
30401         //if(this.inputValue !== undefined){
30402         //this.setValue(this.value);
30403         
30404         
30405         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30406         
30407         
30408         this.grid.render();
30409         this.grid.getDataSource().on('remove', this.refreshValue, this);
30410         this.grid.getDataSource().on('update', this.refreshValue, this);
30411         this.grid.on('afteredit', this.refreshValue, this);
30412  
30413     },
30414      
30415     
30416     /**
30417      * Sets the value of the item. 
30418      * @param {String} either an object  or a string..
30419      */
30420     setValue : function(v){
30421         //this.value = v;
30422         v = v || []; // empty set..
30423         // this does not seem smart - it really only affects memoryproxy grids..
30424         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30425             var ds = this.grid.getDataSource();
30426             // assumes a json reader..
30427             var data = {}
30428             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30429             ds.loadData( data);
30430         }
30431         // clear selection so it does not get stale.
30432         if (this.grid.sm) { 
30433             this.grid.sm.clearSelections();
30434         }
30435         
30436         Roo.form.GridField.superclass.setValue.call(this, v);
30437         this.refreshValue();
30438         // should load data in the grid really....
30439     },
30440     
30441     // private
30442     refreshValue: function() {
30443          var val = [];
30444         this.grid.getDataSource().each(function(r) {
30445             val.push(r.data);
30446         });
30447         this.el.dom.value = Roo.encode(val);
30448     }
30449     
30450      
30451     
30452     
30453 });/*
30454  * Based on:
30455  * Ext JS Library 1.1.1
30456  * Copyright(c) 2006-2007, Ext JS, LLC.
30457  *
30458  * Originally Released Under LGPL - original licence link has changed is not relivant.
30459  *
30460  * Fork - LGPL
30461  * <script type="text/javascript">
30462  */
30463 /**
30464  * @class Roo.form.DisplayField
30465  * @extends Roo.form.Field
30466  * A generic Field to display non-editable data.
30467  * @constructor
30468  * Creates a new Display Field item.
30469  * @param {Object} config Configuration options
30470  */
30471 Roo.form.DisplayField = function(config){
30472     Roo.form.DisplayField.superclass.constructor.call(this, config);
30473     
30474 };
30475
30476 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30477     inputType:      'hidden',
30478     allowBlank:     true,
30479     readOnly:         true,
30480     
30481  
30482     /**
30483      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30484      */
30485     focusClass : undefined,
30486     /**
30487      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30488      */
30489     fieldClass: 'x-form-field',
30490     
30491      /**
30492      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30493      */
30494     valueRenderer: undefined,
30495     
30496     width: 100,
30497     /**
30498      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30499      * {tag: "input", type: "checkbox", autocomplete: "off"})
30500      */
30501      
30502  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30503
30504     onResize : function(){
30505         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30506         
30507     },
30508
30509     initEvents : function(){
30510         // Roo.form.Checkbox.superclass.initEvents.call(this);
30511         // has no events...
30512        
30513     },
30514
30515
30516     getResizeEl : function(){
30517         return this.wrap;
30518     },
30519
30520     getPositionEl : function(){
30521         return this.wrap;
30522     },
30523
30524     // private
30525     onRender : function(ct, position){
30526         
30527         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30528         //if(this.inputValue !== undefined){
30529         this.wrap = this.el.wrap();
30530         
30531         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30532         
30533         if (this.bodyStyle) {
30534             this.viewEl.applyStyles(this.bodyStyle);
30535         }
30536         //this.viewEl.setStyle('padding', '2px');
30537         
30538         this.setValue(this.value);
30539         
30540     },
30541 /*
30542     // private
30543     initValue : Roo.emptyFn,
30544
30545   */
30546
30547         // private
30548     onClick : function(){
30549         
30550     },
30551
30552     /**
30553      * Sets the checked state of the checkbox.
30554      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30555      */
30556     setValue : function(v){
30557         this.value = v;
30558         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30559         // this might be called before we have a dom element..
30560         if (!this.viewEl) {
30561             return;
30562         }
30563         this.viewEl.dom.innerHTML = html;
30564         Roo.form.DisplayField.superclass.setValue.call(this, v);
30565
30566     }
30567 });/*
30568  * 
30569  * Licence- LGPL
30570  * 
30571  */
30572
30573 /**
30574  * @class Roo.form.DayPicker
30575  * @extends Roo.form.Field
30576  * A Day picker show [M] [T] [W] ....
30577  * @constructor
30578  * Creates a new Day Picker
30579  * @param {Object} config Configuration options
30580  */
30581 Roo.form.DayPicker= function(config){
30582     Roo.form.DayPicker.superclass.constructor.call(this, config);
30583      
30584 };
30585
30586 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30587     /**
30588      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30589      */
30590     focusClass : undefined,
30591     /**
30592      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30593      */
30594     fieldClass: "x-form-field",
30595    
30596     /**
30597      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30598      * {tag: "input", type: "checkbox", autocomplete: "off"})
30599      */
30600     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30601     
30602    
30603     actionMode : 'viewEl', 
30604     //
30605     // private
30606  
30607     inputType : 'hidden',
30608     
30609      
30610     inputElement: false, // real input element?
30611     basedOn: false, // ????
30612     
30613     isFormField: true, // not sure where this is needed!!!!
30614
30615     onResize : function(){
30616         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30617         if(!this.boxLabel){
30618             this.el.alignTo(this.wrap, 'c-c');
30619         }
30620     },
30621
30622     initEvents : function(){
30623         Roo.form.Checkbox.superclass.initEvents.call(this);
30624         this.el.on("click", this.onClick,  this);
30625         this.el.on("change", this.onClick,  this);
30626     },
30627
30628
30629     getResizeEl : function(){
30630         return this.wrap;
30631     },
30632
30633     getPositionEl : function(){
30634         return this.wrap;
30635     },
30636
30637     
30638     // private
30639     onRender : function(ct, position){
30640         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30641        
30642         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30643         
30644         var r1 = '<table><tr>';
30645         var r2 = '<tr class="x-form-daypick-icons">';
30646         for (var i=0; i < 7; i++) {
30647             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30648             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30649         }
30650         
30651         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30652         viewEl.select('img').on('click', this.onClick, this);
30653         this.viewEl = viewEl;   
30654         
30655         
30656         // this will not work on Chrome!!!
30657         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30658         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30659         
30660         
30661           
30662
30663     },
30664
30665     // private
30666     initValue : Roo.emptyFn,
30667
30668     /**
30669      * Returns the checked state of the checkbox.
30670      * @return {Boolean} True if checked, else false
30671      */
30672     getValue : function(){
30673         return this.el.dom.value;
30674         
30675     },
30676
30677         // private
30678     onClick : function(e){ 
30679         //this.setChecked(!this.checked);
30680         Roo.get(e.target).toggleClass('x-menu-item-checked');
30681         this.refreshValue();
30682         //if(this.el.dom.checked != this.checked){
30683         //    this.setValue(this.el.dom.checked);
30684        // }
30685     },
30686     
30687     // private
30688     refreshValue : function()
30689     {
30690         var val = '';
30691         this.viewEl.select('img',true).each(function(e,i,n)  {
30692             val += e.is(".x-menu-item-checked") ? String(n) : '';
30693         });
30694         this.setValue(val, true);
30695     },
30696
30697     /**
30698      * Sets the checked state of the checkbox.
30699      * On is always based on a string comparison between inputValue and the param.
30700      * @param {Boolean/String} value - the value to set 
30701      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30702      */
30703     setValue : function(v,suppressEvent){
30704         if (!this.el.dom) {
30705             return;
30706         }
30707         var old = this.el.dom.value ;
30708         this.el.dom.value = v;
30709         if (suppressEvent) {
30710             return ;
30711         }
30712          
30713         // update display..
30714         this.viewEl.select('img',true).each(function(e,i,n)  {
30715             
30716             var on = e.is(".x-menu-item-checked");
30717             var newv = v.indexOf(String(n)) > -1;
30718             if (on != newv) {
30719                 e.toggleClass('x-menu-item-checked');
30720             }
30721             
30722         });
30723         
30724         
30725         this.fireEvent('change', this, v, old);
30726         
30727         
30728     },
30729    
30730     // handle setting of hidden value by some other method!!?!?
30731     setFromHidden: function()
30732     {
30733         if(!this.el){
30734             return;
30735         }
30736         //console.log("SET FROM HIDDEN");
30737         //alert('setFrom hidden');
30738         this.setValue(this.el.dom.value);
30739     },
30740     
30741     onDestroy : function()
30742     {
30743         if(this.viewEl){
30744             Roo.get(this.viewEl).remove();
30745         }
30746          
30747         Roo.form.DayPicker.superclass.onDestroy.call(this);
30748     }
30749
30750 });/*
30751  * RooJS Library 1.1.1
30752  * Copyright(c) 2008-2011  Alan Knowles
30753  *
30754  * License - LGPL
30755  */
30756  
30757
30758 /**
30759  * @class Roo.form.ComboCheck
30760  * @extends Roo.form.ComboBox
30761  * A combobox for multiple select items.
30762  *
30763  * FIXME - could do with a reset button..
30764  * 
30765  * @constructor
30766  * Create a new ComboCheck
30767  * @param {Object} config Configuration options
30768  */
30769 Roo.form.ComboCheck = function(config){
30770     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30771     // should verify some data...
30772     // like
30773     // hiddenName = required..
30774     // displayField = required
30775     // valudField == required
30776     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30777     var _t = this;
30778     Roo.each(req, function(e) {
30779         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30780             throw "Roo.form.ComboCheck : missing value for: " + e;
30781         }
30782     });
30783     
30784     
30785 };
30786
30787 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30788      
30789      
30790     editable : false,
30791      
30792     selectedClass: 'x-menu-item-checked', 
30793     
30794     // private
30795     onRender : function(ct, position){
30796         var _t = this;
30797         
30798         
30799         
30800         if(!this.tpl){
30801             var cls = 'x-combo-list';
30802
30803             
30804             this.tpl =  new Roo.Template({
30805                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30806                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30807                    '<span>{' + this.displayField + '}</span>' +
30808                     '</div>' 
30809                 
30810             });
30811         }
30812  
30813         
30814         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30815         this.view.singleSelect = false;
30816         this.view.multiSelect = true;
30817         this.view.toggleSelect = true;
30818         this.pageTb.add(new Roo.Toolbar.Fill(), {
30819             
30820             text: 'Done',
30821             handler: function()
30822             {
30823                 _t.collapse();
30824             }
30825         });
30826     },
30827     
30828     onViewOver : function(e, t){
30829         // do nothing...
30830         return;
30831         
30832     },
30833     
30834     onViewClick : function(doFocus,index){
30835         return;
30836         
30837     },
30838     select: function () {
30839         //Roo.log("SELECT CALLED");
30840     },
30841      
30842     selectByValue : function(xv, scrollIntoView){
30843         var ar = this.getValueArray();
30844         var sels = [];
30845         
30846         Roo.each(ar, function(v) {
30847             if(v === undefined || v === null){
30848                 return;
30849             }
30850             var r = this.findRecord(this.valueField, v);
30851             if(r){
30852                 sels.push(this.store.indexOf(r))
30853                 
30854             }
30855         },this);
30856         this.view.select(sels);
30857         return false;
30858     },
30859     
30860     
30861     
30862     onSelect : function(record, index){
30863        // Roo.log("onselect Called");
30864        // this is only called by the clear button now..
30865         this.view.clearSelections();
30866         this.setValue('[]');
30867         if (this.value != this.valueBefore) {
30868             this.fireEvent('change', this, this.value, this.valueBefore);
30869             this.valueBefore = this.value;
30870         }
30871     },
30872     getValueArray : function()
30873     {
30874         var ar = [] ;
30875         
30876         try {
30877             //Roo.log(this.value);
30878             if (typeof(this.value) == 'undefined') {
30879                 return [];
30880             }
30881             var ar = Roo.decode(this.value);
30882             return  ar instanceof Array ? ar : []; //?? valid?
30883             
30884         } catch(e) {
30885             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30886             return [];
30887         }
30888          
30889     },
30890     expand : function ()
30891     {
30892         
30893         Roo.form.ComboCheck.superclass.expand.call(this);
30894         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30895         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30896         
30897
30898     },
30899     
30900     collapse : function(){
30901         Roo.form.ComboCheck.superclass.collapse.call(this);
30902         var sl = this.view.getSelectedIndexes();
30903         var st = this.store;
30904         var nv = [];
30905         var tv = [];
30906         var r;
30907         Roo.each(sl, function(i) {
30908             r = st.getAt(i);
30909             nv.push(r.get(this.valueField));
30910         },this);
30911         this.setValue(Roo.encode(nv));
30912         if (this.value != this.valueBefore) {
30913
30914             this.fireEvent('change', this, this.value, this.valueBefore);
30915             this.valueBefore = this.value;
30916         }
30917         
30918     },
30919     
30920     setValue : function(v){
30921         // Roo.log(v);
30922         this.value = v;
30923         
30924         var vals = this.getValueArray();
30925         var tv = [];
30926         Roo.each(vals, function(k) {
30927             var r = this.findRecord(this.valueField, k);
30928             if(r){
30929                 tv.push(r.data[this.displayField]);
30930             }else if(this.valueNotFoundText !== undefined){
30931                 tv.push( this.valueNotFoundText );
30932             }
30933         },this);
30934        // Roo.log(tv);
30935         
30936         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30937         this.hiddenField.value = v;
30938         this.value = v;
30939     }
30940     
30941 });/*
30942  * Based on:
30943  * Ext JS Library 1.1.1
30944  * Copyright(c) 2006-2007, Ext JS, LLC.
30945  *
30946  * Originally Released Under LGPL - original licence link has changed is not relivant.
30947  *
30948  * Fork - LGPL
30949  * <script type="text/javascript">
30950  */
30951  
30952 /**
30953  * @class Roo.form.Signature
30954  * @extends Roo.form.Field
30955  * Signature field.  
30956  * @constructor
30957  * 
30958  * @param {Object} config Configuration options
30959  */
30960
30961 Roo.form.Signature = function(config){
30962     Roo.form.Signature.superclass.constructor.call(this, config);
30963     
30964     this.addEvents({// not in used??
30965          /**
30966          * @event confirm
30967          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30968              * @param {Roo.form.Signature} combo This combo box
30969              */
30970         'confirm' : true,
30971         /**
30972          * @event reset
30973          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30974              * @param {Roo.form.ComboBox} combo This combo box
30975              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30976              */
30977         'reset' : true
30978     });
30979 };
30980
30981 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30982     /**
30983      * @cfg {Object} labels Label to use when rendering a form.
30984      * defaults to 
30985      * labels : { 
30986      *      clear : "Clear",
30987      *      confirm : "Confirm"
30988      *  }
30989      */
30990     labels : { 
30991         clear : "Clear",
30992         confirm : "Confirm"
30993     },
30994     /**
30995      * @cfg {Number} width The signature panel width (defaults to 300)
30996      */
30997     width: 300,
30998     /**
30999      * @cfg {Number} height The signature panel height (defaults to 100)
31000      */
31001     height : 100,
31002     /**
31003      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31004      */
31005     allowBlank : false,
31006     
31007     //private
31008     // {Object} signPanel The signature SVG panel element (defaults to {})
31009     signPanel : {},
31010     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31011     isMouseDown : false,
31012     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31013     isConfirmed : false,
31014     // {String} signatureTmp SVG mapping string (defaults to empty string)
31015     signatureTmp : '',
31016     
31017     
31018     defaultAutoCreate : { // modified by initCompnoent..
31019         tag: "input",
31020         type:"hidden"
31021     },
31022
31023     // private
31024     onRender : function(ct, position){
31025         
31026         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31027         
31028         this.wrap = this.el.wrap({
31029             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31030         });
31031         
31032         this.createToolbar(this);
31033         this.signPanel = this.wrap.createChild({
31034                 tag: 'div',
31035                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31036             }, this.el
31037         );
31038             
31039         this.svgID = Roo.id();
31040         this.svgEl = this.signPanel.createChild({
31041               xmlns : 'http://www.w3.org/2000/svg',
31042               tag : 'svg',
31043               id : this.svgID + "-svg",
31044               width: this.width,
31045               height: this.height,
31046               viewBox: '0 0 '+this.width+' '+this.height,
31047               cn : [
31048                 {
31049                     tag: "rect",
31050                     id: this.svgID + "-svg-r",
31051                     width: this.width,
31052                     height: this.height,
31053                     fill: "#ffa"
31054                 },
31055                 {
31056                     tag: "line",
31057                     id: this.svgID + "-svg-l",
31058                     x1: "0", // start
31059                     y1: (this.height*0.8), // start set the line in 80% of height
31060                     x2: this.width, // end
31061                     y2: (this.height*0.8), // end set the line in 80% of height
31062                     'stroke': "#666",
31063                     'stroke-width': "1",
31064                     'stroke-dasharray': "3",
31065                     'shape-rendering': "crispEdges",
31066                     'pointer-events': "none"
31067                 },
31068                 {
31069                     tag: "path",
31070                     id: this.svgID + "-svg-p",
31071                     'stroke': "navy",
31072                     'stroke-width': "3",
31073                     'fill': "none",
31074                     'pointer-events': 'none'
31075                 }
31076               ]
31077         });
31078         this.createSVG();
31079         this.svgBox = this.svgEl.dom.getScreenCTM();
31080     },
31081     createSVG : function(){ 
31082         var svg = this.signPanel;
31083         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31084         var t = this;
31085
31086         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31087         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31088         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31089         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31090         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31091         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31092         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31093         
31094     },
31095     isTouchEvent : function(e){
31096         return e.type.match(/^touch/);
31097     },
31098     getCoords : function (e) {
31099         var pt    = this.svgEl.dom.createSVGPoint();
31100         pt.x = e.clientX; 
31101         pt.y = e.clientY;
31102         if (this.isTouchEvent(e)) {
31103             pt.x =  e.targetTouches[0].clientX 
31104             pt.y = e.targetTouches[0].clientY;
31105         }
31106         var a = this.svgEl.dom.getScreenCTM();
31107         var b = a.inverse();
31108         var mx = pt.matrixTransform(b);
31109         return mx.x + ',' + mx.y;
31110     },
31111     //mouse event headler 
31112     down : function (e) {
31113         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31114         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31115         
31116         this.isMouseDown = true;
31117         
31118         e.preventDefault();
31119     },
31120     move : function (e) {
31121         if (this.isMouseDown) {
31122             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31123             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31124         }
31125         
31126         e.preventDefault();
31127     },
31128     up : function (e) {
31129         this.isMouseDown = false;
31130         var sp = this.signatureTmp.split(' ');
31131         
31132         if(sp.length > 1){
31133             if(!sp[sp.length-2].match(/^L/)){
31134                 sp.pop();
31135                 sp.pop();
31136                 sp.push("");
31137                 this.signatureTmp = sp.join(" ");
31138             }
31139         }
31140         if(this.getValue() != this.signatureTmp){
31141             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31142             this.isConfirmed = false;
31143         }
31144         e.preventDefault();
31145     },
31146     
31147     /**
31148      * Protected method that will not generally be called directly. It
31149      * is called when the editor creates its toolbar. Override this method if you need to
31150      * add custom toolbar buttons.
31151      * @param {HtmlEditor} editor
31152      */
31153     createToolbar : function(editor){
31154          function btn(id, toggle, handler){
31155             var xid = fid + '-'+ id ;
31156             return {
31157                 id : xid,
31158                 cmd : id,
31159                 cls : 'x-btn-icon x-edit-'+id,
31160                 enableToggle:toggle !== false,
31161                 scope: editor, // was editor...
31162                 handler:handler||editor.relayBtnCmd,
31163                 clickEvent:'mousedown',
31164                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31165                 tabIndex:-1
31166             };
31167         }
31168         
31169         
31170         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31171         this.tb = tb;
31172         this.tb.add(
31173            {
31174                 cls : ' x-signature-btn x-signature-'+id,
31175                 scope: editor, // was editor...
31176                 handler: this.reset,
31177                 clickEvent:'mousedown',
31178                 text: this.labels.clear
31179             },
31180             {
31181                  xtype : 'Fill',
31182                  xns: Roo.Toolbar
31183             }, 
31184             {
31185                 cls : '  x-signature-btn x-signature-'+id,
31186                 scope: editor, // was editor...
31187                 handler: this.confirmHandler,
31188                 clickEvent:'mousedown',
31189                 text: this.labels.confirm
31190             }
31191         );
31192     
31193     },
31194     //public
31195     /**
31196      * when user is clicked confirm then show this image.....
31197      * 
31198      * @return {String} Image Data URI
31199      */
31200     getImageDataURI : function(){
31201         var svg = this.svgEl.dom.parentNode.innerHTML;
31202         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31203         return src; 
31204     },
31205     /**
31206      * 
31207      * @return {Boolean} this.isConfirmed
31208      */
31209     getConfirmed : function(){
31210         return this.isConfirmed;
31211     },
31212     /**
31213      * 
31214      * @return {Number} this.width
31215      */
31216     getWidth : function(){
31217         return this.width;
31218     },
31219     /**
31220      * 
31221      * @return {Number} this.height
31222      */
31223     getHeight : function(){
31224         return this.height;
31225     },
31226     // private
31227     getSignature : function(){
31228         return this.signatureTmp;
31229     },
31230     // private
31231     reset : function(){
31232         this.signatureTmp = '';
31233         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31234         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31235         this.isConfirmed = false;
31236         Roo.form.Signature.superclass.reset.call(this);
31237     },
31238     setSignature : function(s){
31239         this.signatureTmp = s;
31240         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31241         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31242         this.setValue(s);
31243         this.isConfirmed = false;
31244         Roo.form.Signature.superclass.reset.call(this);
31245     }, 
31246     test : function(){
31247 //        Roo.log(this.signPanel.dom.contentWindow.up())
31248     },
31249     //private
31250     setConfirmed : function(){
31251         
31252         
31253         
31254 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31255     },
31256     // private
31257     confirmHandler : function(){
31258         if(!this.getSignature()){
31259             return;
31260         }
31261         
31262         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31263         this.setValue(this.getSignature());
31264         this.isConfirmed = true;
31265         
31266         this.fireEvent('confirm', this);
31267     },
31268     // private
31269     // Subclasses should provide the validation implementation by overriding this
31270     validateValue : function(value){
31271         if(this.allowBlank){
31272             return true;
31273         }
31274         
31275         if(this.isConfirmed){
31276             return true;
31277         }
31278         return false;
31279     }
31280 });/*
31281  * Based on:
31282  * Ext JS Library 1.1.1
31283  * Copyright(c) 2006-2007, Ext JS, LLC.
31284  *
31285  * Originally Released Under LGPL - original licence link has changed is not relivant.
31286  *
31287  * Fork - LGPL
31288  * <script type="text/javascript">
31289  */
31290  
31291
31292 /**
31293  * @class Roo.form.ComboBox
31294  * @extends Roo.form.TriggerField
31295  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31296  * @constructor
31297  * Create a new ComboBox.
31298  * @param {Object} config Configuration options
31299  */
31300 Roo.form.Select = function(config){
31301     Roo.form.Select.superclass.constructor.call(this, config);
31302      
31303 };
31304
31305 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31306     /**
31307      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31308      */
31309     /**
31310      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31311      * rendering into an Roo.Editor, defaults to false)
31312      */
31313     /**
31314      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31315      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31316      */
31317     /**
31318      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31319      */
31320     /**
31321      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31322      * the dropdown list (defaults to undefined, with no header element)
31323      */
31324
31325      /**
31326      * @cfg {String/Roo.Template} tpl The template to use to render the output
31327      */
31328      
31329     // private
31330     defaultAutoCreate : {tag: "select"  },
31331     /**
31332      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31333      */
31334     listWidth: undefined,
31335     /**
31336      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31337      * mode = 'remote' or 'text' if mode = 'local')
31338      */
31339     displayField: undefined,
31340     /**
31341      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31342      * mode = 'remote' or 'value' if mode = 'local'). 
31343      * Note: use of a valueField requires the user make a selection
31344      * in order for a value to be mapped.
31345      */
31346     valueField: undefined,
31347     
31348     
31349     /**
31350      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31351      * field's data value (defaults to the underlying DOM element's name)
31352      */
31353     hiddenName: undefined,
31354     /**
31355      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31356      */
31357     listClass: '',
31358     /**
31359      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31360      */
31361     selectedClass: 'x-combo-selected',
31362     /**
31363      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31364      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31365      * which displays a downward arrow icon).
31366      */
31367     triggerClass : 'x-form-arrow-trigger',
31368     /**
31369      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31370      */
31371     shadow:'sides',
31372     /**
31373      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31374      * anchor positions (defaults to 'tl-bl')
31375      */
31376     listAlign: 'tl-bl?',
31377     /**
31378      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31379      */
31380     maxHeight: 300,
31381     /**
31382      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31383      * query specified by the allQuery config option (defaults to 'query')
31384      */
31385     triggerAction: 'query',
31386     /**
31387      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31388      * (defaults to 4, does not apply if editable = false)
31389      */
31390     minChars : 4,
31391     /**
31392      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31393      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31394      */
31395     typeAhead: false,
31396     /**
31397      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31398      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31399      */
31400     queryDelay: 500,
31401     /**
31402      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31403      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31404      */
31405     pageSize: 0,
31406     /**
31407      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31408      * when editable = true (defaults to false)
31409      */
31410     selectOnFocus:false,
31411     /**
31412      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31413      */
31414     queryParam: 'query',
31415     /**
31416      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31417      * when mode = 'remote' (defaults to 'Loading...')
31418      */
31419     loadingText: 'Loading...',
31420     /**
31421      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31422      */
31423     resizable: false,
31424     /**
31425      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31426      */
31427     handleHeight : 8,
31428     /**
31429      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31430      * traditional select (defaults to true)
31431      */
31432     editable: true,
31433     /**
31434      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31435      */
31436     allQuery: '',
31437     /**
31438      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31439      */
31440     mode: 'remote',
31441     /**
31442      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31443      * listWidth has a higher value)
31444      */
31445     minListWidth : 70,
31446     /**
31447      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31448      * allow the user to set arbitrary text into the field (defaults to false)
31449      */
31450     forceSelection:false,
31451     /**
31452      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31453      * if typeAhead = true (defaults to 250)
31454      */
31455     typeAheadDelay : 250,
31456     /**
31457      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31458      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31459      */
31460     valueNotFoundText : undefined,
31461     
31462     /**
31463      * @cfg {String} defaultValue The value displayed after loading the store.
31464      */
31465     defaultValue: '',
31466     
31467     /**
31468      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31469      */
31470     blockFocus : false,
31471     
31472     /**
31473      * @cfg {Boolean} disableClear Disable showing of clear button.
31474      */
31475     disableClear : false,
31476     /**
31477      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31478      */
31479     alwaysQuery : false,
31480     
31481     //private
31482     addicon : false,
31483     editicon: false,
31484     
31485     // element that contains real text value.. (when hidden is used..)
31486      
31487     // private
31488     onRender : function(ct, position){
31489         Roo.form.Field.prototype.onRender.call(this, ct, position);
31490         
31491         if(this.store){
31492             this.store.on('beforeload', this.onBeforeLoad, this);
31493             this.store.on('load', this.onLoad, this);
31494             this.store.on('loadexception', this.onLoadException, this);
31495             this.store.load({});
31496         }
31497         
31498         
31499         
31500     },
31501
31502     // private
31503     initEvents : function(){
31504         //Roo.form.ComboBox.superclass.initEvents.call(this);
31505  
31506     },
31507
31508     onDestroy : function(){
31509        
31510         if(this.store){
31511             this.store.un('beforeload', this.onBeforeLoad, this);
31512             this.store.un('load', this.onLoad, this);
31513             this.store.un('loadexception', this.onLoadException, this);
31514         }
31515         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31516     },
31517
31518     // private
31519     fireKey : function(e){
31520         if(e.isNavKeyPress() && !this.list.isVisible()){
31521             this.fireEvent("specialkey", this, e);
31522         }
31523     },
31524
31525     // private
31526     onResize: function(w, h){
31527         
31528         return; 
31529     
31530         
31531     },
31532
31533     /**
31534      * Allow or prevent the user from directly editing the field text.  If false is passed,
31535      * the user will only be able to select from the items defined in the dropdown list.  This method
31536      * is the runtime equivalent of setting the 'editable' config option at config time.
31537      * @param {Boolean} value True to allow the user to directly edit the field text
31538      */
31539     setEditable : function(value){
31540          
31541     },
31542
31543     // private
31544     onBeforeLoad : function(){
31545         
31546         Roo.log("Select before load");
31547         return;
31548     
31549         this.innerList.update(this.loadingText ?
31550                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31551         //this.restrictHeight();
31552         this.selectedIndex = -1;
31553     },
31554
31555     // private
31556     onLoad : function(){
31557
31558     
31559         var dom = this.el.dom;
31560         dom.innerHTML = '';
31561          var od = dom.ownerDocument;
31562          
31563         if (this.emptyText) {
31564             var op = od.createElement('option');
31565             op.setAttribute('value', '');
31566             op.innerHTML = String.format('{0}', this.emptyText);
31567             dom.appendChild(op);
31568         }
31569         if(this.store.getCount() > 0){
31570            
31571             var vf = this.valueField;
31572             var df = this.displayField;
31573             this.store.data.each(function(r) {
31574                 // which colmsn to use... testing - cdoe / title..
31575                 var op = od.createElement('option');
31576                 op.setAttribute('value', r.data[vf]);
31577                 op.innerHTML = String.format('{0}', r.data[df]);
31578                 dom.appendChild(op);
31579             });
31580             if (typeof(this.defaultValue != 'undefined')) {
31581                 this.setValue(this.defaultValue);
31582             }
31583             
31584              
31585         }else{
31586             //this.onEmptyResults();
31587         }
31588         //this.el.focus();
31589     },
31590     // private
31591     onLoadException : function()
31592     {
31593         dom.innerHTML = '';
31594             
31595         Roo.log("Select on load exception");
31596         return;
31597     
31598         this.collapse();
31599         Roo.log(this.store.reader.jsonData);
31600         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31601             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31602         }
31603         
31604         
31605     },
31606     // private
31607     onTypeAhead : function(){
31608          
31609     },
31610
31611     // private
31612     onSelect : function(record, index){
31613         Roo.log('on select?');
31614         return;
31615         if(this.fireEvent('beforeselect', this, record, index) !== false){
31616             this.setFromData(index > -1 ? record.data : false);
31617             this.collapse();
31618             this.fireEvent('select', this, record, index);
31619         }
31620     },
31621
31622     /**
31623      * Returns the currently selected field value or empty string if no value is set.
31624      * @return {String} value The selected value
31625      */
31626     getValue : function(){
31627         var dom = this.el.dom;
31628         this.value = dom.options[dom.selectedIndex].value;
31629         return this.value;
31630         
31631     },
31632
31633     /**
31634      * Clears any text/value currently set in the field
31635      */
31636     clearValue : function(){
31637         this.value = '';
31638         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31639         
31640     },
31641
31642     /**
31643      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31644      * will be displayed in the field.  If the value does not match the data value of an existing item,
31645      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31646      * Otherwise the field will be blank (although the value will still be set).
31647      * @param {String} value The value to match
31648      */
31649     setValue : function(v){
31650         var d = this.el.dom;
31651         for (var i =0; i < d.options.length;i++) {
31652             if (v == d.options[i].value) {
31653                 d.selectedIndex = i;
31654                 this.value = v;
31655                 return;
31656             }
31657         }
31658         this.clearValue();
31659     },
31660     /**
31661      * @property {Object} the last set data for the element
31662      */
31663     
31664     lastData : false,
31665     /**
31666      * Sets the value of the field based on a object which is related to the record format for the store.
31667      * @param {Object} value the value to set as. or false on reset?
31668      */
31669     setFromData : function(o){
31670         Roo.log('setfrom data?');
31671          
31672         
31673         
31674     },
31675     // private
31676     reset : function(){
31677         this.clearValue();
31678     },
31679     // private
31680     findRecord : function(prop, value){
31681         
31682         return false;
31683     
31684         var record;
31685         if(this.store.getCount() > 0){
31686             this.store.each(function(r){
31687                 if(r.data[prop] == value){
31688                     record = r;
31689                     return false;
31690                 }
31691                 return true;
31692             });
31693         }
31694         return record;
31695     },
31696     
31697     getName: function()
31698     {
31699         // returns hidden if it's set..
31700         if (!this.rendered) {return ''};
31701         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31702         
31703     },
31704      
31705
31706     
31707
31708     // private
31709     onEmptyResults : function(){
31710         Roo.log('empty results');
31711         //this.collapse();
31712     },
31713
31714     /**
31715      * Returns true if the dropdown list is expanded, else false.
31716      */
31717     isExpanded : function(){
31718         return false;
31719     },
31720
31721     /**
31722      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31723      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31724      * @param {String} value The data value of the item to select
31725      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31726      * selected item if it is not currently in view (defaults to true)
31727      * @return {Boolean} True if the value matched an item in the list, else false
31728      */
31729     selectByValue : function(v, scrollIntoView){
31730         Roo.log('select By Value');
31731         return false;
31732     
31733         if(v !== undefined && v !== null){
31734             var r = this.findRecord(this.valueField || this.displayField, v);
31735             if(r){
31736                 this.select(this.store.indexOf(r), scrollIntoView);
31737                 return true;
31738             }
31739         }
31740         return false;
31741     },
31742
31743     /**
31744      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31745      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31746      * @param {Number} index The zero-based index of the list item to select
31747      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31748      * selected item if it is not currently in view (defaults to true)
31749      */
31750     select : function(index, scrollIntoView){
31751         Roo.log('select ');
31752         return  ;
31753         
31754         this.selectedIndex = index;
31755         this.view.select(index);
31756         if(scrollIntoView !== false){
31757             var el = this.view.getNode(index);
31758             if(el){
31759                 this.innerList.scrollChildIntoView(el, false);
31760             }
31761         }
31762     },
31763
31764       
31765
31766     // private
31767     validateBlur : function(){
31768         
31769         return;
31770         
31771     },
31772
31773     // private
31774     initQuery : function(){
31775         this.doQuery(this.getRawValue());
31776     },
31777
31778     // private
31779     doForce : function(){
31780         if(this.el.dom.value.length > 0){
31781             this.el.dom.value =
31782                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31783              
31784         }
31785     },
31786
31787     /**
31788      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31789      * query allowing the query action to be canceled if needed.
31790      * @param {String} query The SQL query to execute
31791      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31792      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31793      * saved in the current store (defaults to false)
31794      */
31795     doQuery : function(q, forceAll){
31796         
31797         Roo.log('doQuery?');
31798         if(q === undefined || q === null){
31799             q = '';
31800         }
31801         var qe = {
31802             query: q,
31803             forceAll: forceAll,
31804             combo: this,
31805             cancel:false
31806         };
31807         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31808             return false;
31809         }
31810         q = qe.query;
31811         forceAll = qe.forceAll;
31812         if(forceAll === true || (q.length >= this.minChars)){
31813             if(this.lastQuery != q || this.alwaysQuery){
31814                 this.lastQuery = q;
31815                 if(this.mode == 'local'){
31816                     this.selectedIndex = -1;
31817                     if(forceAll){
31818                         this.store.clearFilter();
31819                     }else{
31820                         this.store.filter(this.displayField, q);
31821                     }
31822                     this.onLoad();
31823                 }else{
31824                     this.store.baseParams[this.queryParam] = q;
31825                     this.store.load({
31826                         params: this.getParams(q)
31827                     });
31828                     this.expand();
31829                 }
31830             }else{
31831                 this.selectedIndex = -1;
31832                 this.onLoad();   
31833             }
31834         }
31835     },
31836
31837     // private
31838     getParams : function(q){
31839         var p = {};
31840         //p[this.queryParam] = q;
31841         if(this.pageSize){
31842             p.start = 0;
31843             p.limit = this.pageSize;
31844         }
31845         return p;
31846     },
31847
31848     /**
31849      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31850      */
31851     collapse : function(){
31852         
31853     },
31854
31855     // private
31856     collapseIf : function(e){
31857         
31858     },
31859
31860     /**
31861      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31862      */
31863     expand : function(){
31864         
31865     } ,
31866
31867     // private
31868      
31869
31870     /** 
31871     * @cfg {Boolean} grow 
31872     * @hide 
31873     */
31874     /** 
31875     * @cfg {Number} growMin 
31876     * @hide 
31877     */
31878     /** 
31879     * @cfg {Number} growMax 
31880     * @hide 
31881     */
31882     /**
31883      * @hide
31884      * @method autoSize
31885      */
31886     
31887     setWidth : function()
31888     {
31889         
31890     },
31891     getResizeEl : function(){
31892         return this.el;
31893     }
31894 });//<script type="text/javasscript">
31895  
31896
31897 /**
31898  * @class Roo.DDView
31899  * A DnD enabled version of Roo.View.
31900  * @param {Element/String} container The Element in which to create the View.
31901  * @param {String} tpl The template string used to create the markup for each element of the View
31902  * @param {Object} config The configuration properties. These include all the config options of
31903  * {@link Roo.View} plus some specific to this class.<br>
31904  * <p>
31905  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31906  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31907  * <p>
31908  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31909 .x-view-drag-insert-above {
31910         border-top:1px dotted #3366cc;
31911 }
31912 .x-view-drag-insert-below {
31913         border-bottom:1px dotted #3366cc;
31914 }
31915 </code></pre>
31916  * 
31917  */
31918  
31919 Roo.DDView = function(container, tpl, config) {
31920     Roo.DDView.superclass.constructor.apply(this, arguments);
31921     this.getEl().setStyle("outline", "0px none");
31922     this.getEl().unselectable();
31923     if (this.dragGroup) {
31924                 this.setDraggable(this.dragGroup.split(","));
31925     }
31926     if (this.dropGroup) {
31927                 this.setDroppable(this.dropGroup.split(","));
31928     }
31929     if (this.deletable) {
31930         this.setDeletable();
31931     }
31932     this.isDirtyFlag = false;
31933         this.addEvents({
31934                 "drop" : true
31935         });
31936 };
31937
31938 Roo.extend(Roo.DDView, Roo.View, {
31939 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31940 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31941 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31942 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31943
31944         isFormField: true,
31945
31946         reset: Roo.emptyFn,
31947         
31948         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31949
31950         validate: function() {
31951                 return true;
31952         },
31953         
31954         destroy: function() {
31955                 this.purgeListeners();
31956                 this.getEl.removeAllListeners();
31957                 this.getEl().remove();
31958                 if (this.dragZone) {
31959                         if (this.dragZone.destroy) {
31960                                 this.dragZone.destroy();
31961                         }
31962                 }
31963                 if (this.dropZone) {
31964                         if (this.dropZone.destroy) {
31965                                 this.dropZone.destroy();
31966                         }
31967                 }
31968         },
31969
31970 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31971         getName: function() {
31972                 return this.name;
31973         },
31974
31975 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31976         setValue: function(v) {
31977                 if (!this.store) {
31978                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31979                 }
31980                 var data = {};
31981                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31982                 this.store.proxy = new Roo.data.MemoryProxy(data);
31983                 this.store.load();
31984         },
31985
31986 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31987         getValue: function() {
31988                 var result = '(';
31989                 this.store.each(function(rec) {
31990                         result += rec.id + ',';
31991                 });
31992                 return result.substr(0, result.length - 1) + ')';
31993         },
31994         
31995         getIds: function() {
31996                 var i = 0, result = new Array(this.store.getCount());
31997                 this.store.each(function(rec) {
31998                         result[i++] = rec.id;
31999                 });
32000                 return result;
32001         },
32002         
32003         isDirty: function() {
32004                 return this.isDirtyFlag;
32005         },
32006
32007 /**
32008  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32009  *      whole Element becomes the target, and this causes the drop gesture to append.
32010  */
32011     getTargetFromEvent : function(e) {
32012                 var target = e.getTarget();
32013                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32014                 target = target.parentNode;
32015                 }
32016                 if (!target) {
32017                         target = this.el.dom.lastChild || this.el.dom;
32018                 }
32019                 return target;
32020     },
32021
32022 /**
32023  *      Create the drag data which consists of an object which has the property "ddel" as
32024  *      the drag proxy element. 
32025  */
32026     getDragData : function(e) {
32027         var target = this.findItemFromChild(e.getTarget());
32028                 if(target) {
32029                         this.handleSelection(e);
32030                         var selNodes = this.getSelectedNodes();
32031             var dragData = {
32032                 source: this,
32033                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32034                 nodes: selNodes,
32035                 records: []
32036                         };
32037                         var selectedIndices = this.getSelectedIndexes();
32038                         for (var i = 0; i < selectedIndices.length; i++) {
32039                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32040                         }
32041                         if (selNodes.length == 1) {
32042                                 dragData.ddel = target.cloneNode(true); // the div element
32043                         } else {
32044                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32045                                 div.className = 'multi-proxy';
32046                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32047                                         div.appendChild(selNodes[i].cloneNode(true));
32048                                 }
32049                                 dragData.ddel = div;
32050                         }
32051             //console.log(dragData)
32052             //console.log(dragData.ddel.innerHTML)
32053                         return dragData;
32054                 }
32055         //console.log('nodragData')
32056                 return false;
32057     },
32058     
32059 /**     Specify to which ddGroup items in this DDView may be dragged. */
32060     setDraggable: function(ddGroup) {
32061         if (ddGroup instanceof Array) {
32062                 Roo.each(ddGroup, this.setDraggable, this);
32063                 return;
32064         }
32065         if (this.dragZone) {
32066                 this.dragZone.addToGroup(ddGroup);
32067         } else {
32068                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32069                                 containerScroll: true,
32070                                 ddGroup: ddGroup 
32071
32072                         });
32073 //                      Draggability implies selection. DragZone's mousedown selects the element.
32074                         if (!this.multiSelect) { this.singleSelect = true; }
32075
32076 //                      Wire the DragZone's handlers up to methods in *this*
32077                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32078                 }
32079     },
32080
32081 /**     Specify from which ddGroup this DDView accepts drops. */
32082     setDroppable: function(ddGroup) {
32083         if (ddGroup instanceof Array) {
32084                 Roo.each(ddGroup, this.setDroppable, this);
32085                 return;
32086         }
32087         if (this.dropZone) {
32088                 this.dropZone.addToGroup(ddGroup);
32089         } else {
32090                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32091                                 containerScroll: true,
32092                                 ddGroup: ddGroup
32093                         });
32094
32095 //                      Wire the DropZone's handlers up to methods in *this*
32096                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32097                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32098                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32099                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32100                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32101                 }
32102     },
32103
32104 /**     Decide whether to drop above or below a View node. */
32105     getDropPoint : function(e, n, dd){
32106         if (n == this.el.dom) { return "above"; }
32107                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32108                 var c = t + (b - t) / 2;
32109                 var y = Roo.lib.Event.getPageY(e);
32110                 if(y <= c) {
32111                         return "above";
32112                 }else{
32113                         return "below";
32114                 }
32115     },
32116
32117     onNodeEnter : function(n, dd, e, data){
32118                 return false;
32119     },
32120     
32121     onNodeOver : function(n, dd, e, data){
32122                 var pt = this.getDropPoint(e, n, dd);
32123                 // set the insert point style on the target node
32124                 var dragElClass = this.dropNotAllowed;
32125                 if (pt) {
32126                         var targetElClass;
32127                         if (pt == "above"){
32128                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32129                                 targetElClass = "x-view-drag-insert-above";
32130                         } else {
32131                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32132                                 targetElClass = "x-view-drag-insert-below";
32133                         }
32134                         if (this.lastInsertClass != targetElClass){
32135                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32136                                 this.lastInsertClass = targetElClass;
32137                         }
32138                 }
32139                 return dragElClass;
32140         },
32141
32142     onNodeOut : function(n, dd, e, data){
32143                 this.removeDropIndicators(n);
32144     },
32145
32146     onNodeDrop : function(n, dd, e, data){
32147         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32148                 return false;
32149         }
32150         var pt = this.getDropPoint(e, n, dd);
32151                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32152                 if (pt == "below") { insertAt++; }
32153                 for (var i = 0; i < data.records.length; i++) {
32154                         var r = data.records[i];
32155                         var dup = this.store.getById(r.id);
32156                         if (dup && (dd != this.dragZone)) {
32157                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32158                         } else {
32159                                 if (data.copy) {
32160                                         this.store.insert(insertAt++, r.copy());
32161                                 } else {
32162                                         data.source.isDirtyFlag = true;
32163                                         r.store.remove(r);
32164                                         this.store.insert(insertAt++, r);
32165                                 }
32166                                 this.isDirtyFlag = true;
32167                         }
32168                 }
32169                 this.dragZone.cachedTarget = null;
32170                 return true;
32171     },
32172
32173     removeDropIndicators : function(n){
32174                 if(n){
32175                         Roo.fly(n).removeClass([
32176                                 "x-view-drag-insert-above",
32177                                 "x-view-drag-insert-below"]);
32178                         this.lastInsertClass = "_noclass";
32179                 }
32180     },
32181
32182 /**
32183  *      Utility method. Add a delete option to the DDView's context menu.
32184  *      @param {String} imageUrl The URL of the "delete" icon image.
32185  */
32186         setDeletable: function(imageUrl) {
32187                 if (!this.singleSelect && !this.multiSelect) {
32188                         this.singleSelect = true;
32189                 }
32190                 var c = this.getContextMenu();
32191                 this.contextMenu.on("itemclick", function(item) {
32192                         switch (item.id) {
32193                                 case "delete":
32194                                         this.remove(this.getSelectedIndexes());
32195                                         break;
32196                         }
32197                 }, this);
32198                 this.contextMenu.add({
32199                         icon: imageUrl,
32200                         id: "delete",
32201                         text: 'Delete'
32202                 });
32203         },
32204         
32205 /**     Return the context menu for this DDView. */
32206         getContextMenu: function() {
32207                 if (!this.contextMenu) {
32208 //                      Create the View's context menu
32209                         this.contextMenu = new Roo.menu.Menu({
32210                                 id: this.id + "-contextmenu"
32211                         });
32212                         this.el.on("contextmenu", this.showContextMenu, this);
32213                 }
32214                 return this.contextMenu;
32215         },
32216         
32217         disableContextMenu: function() {
32218                 if (this.contextMenu) {
32219                         this.el.un("contextmenu", this.showContextMenu, this);
32220                 }
32221         },
32222
32223         showContextMenu: function(e, item) {
32224         item = this.findItemFromChild(e.getTarget());
32225                 if (item) {
32226                         e.stopEvent();
32227                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32228                         this.contextMenu.showAt(e.getXY());
32229             }
32230     },
32231
32232 /**
32233  *      Remove {@link Roo.data.Record}s at the specified indices.
32234  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32235  */
32236     remove: function(selectedIndices) {
32237                 selectedIndices = [].concat(selectedIndices);
32238                 for (var i = 0; i < selectedIndices.length; i++) {
32239                         var rec = this.store.getAt(selectedIndices[i]);
32240                         this.store.remove(rec);
32241                 }
32242     },
32243
32244 /**
32245  *      Double click fires the event, but also, if this is draggable, and there is only one other
32246  *      related DropZone, it transfers the selected node.
32247  */
32248     onDblClick : function(e){
32249         var item = this.findItemFromChild(e.getTarget());
32250         if(item){
32251             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32252                 return false;
32253             }
32254             if (this.dragGroup) {
32255                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32256                     while (targets.indexOf(this.dropZone) > -1) {
32257                             targets.remove(this.dropZone);
32258                                 }
32259                     if (targets.length == 1) {
32260                                         this.dragZone.cachedTarget = null;
32261                         var el = Roo.get(targets[0].getEl());
32262                         var box = el.getBox(true);
32263                         targets[0].onNodeDrop(el.dom, {
32264                                 target: el.dom,
32265                                 xy: [box.x, box.y + box.height - 1]
32266                         }, null, this.getDragData(e));
32267                     }
32268                 }
32269         }
32270     },
32271     
32272     handleSelection: function(e) {
32273                 this.dragZone.cachedTarget = null;
32274         var item = this.findItemFromChild(e.getTarget());
32275         if (!item) {
32276                 this.clearSelections(true);
32277                 return;
32278         }
32279                 if (item && (this.multiSelect || this.singleSelect)){
32280                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32281                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32282                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32283                                 this.unselect(item);
32284                         } else {
32285                                 this.select(item, this.multiSelect && e.ctrlKey);
32286                                 this.lastSelection = item;
32287                         }
32288                 }
32289     },
32290
32291     onItemClick : function(item, index, e){
32292                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32293                         return false;
32294                 }
32295                 return true;
32296     },
32297
32298     unselect : function(nodeInfo, suppressEvent){
32299                 var node = this.getNode(nodeInfo);
32300                 if(node && this.isSelected(node)){
32301                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32302                                 Roo.fly(node).removeClass(this.selectedClass);
32303                                 this.selections.remove(node);
32304                                 if(!suppressEvent){
32305                                         this.fireEvent("selectionchange", this, this.selections);
32306                                 }
32307                         }
32308                 }
32309     }
32310 });
32311 /*
32312  * Based on:
32313  * Ext JS Library 1.1.1
32314  * Copyright(c) 2006-2007, Ext JS, LLC.
32315  *
32316  * Originally Released Under LGPL - original licence link has changed is not relivant.
32317  *
32318  * Fork - LGPL
32319  * <script type="text/javascript">
32320  */
32321  
32322 /**
32323  * @class Roo.LayoutManager
32324  * @extends Roo.util.Observable
32325  * Base class for layout managers.
32326  */
32327 Roo.LayoutManager = function(container, config){
32328     Roo.LayoutManager.superclass.constructor.call(this);
32329     this.el = Roo.get(container);
32330     // ie scrollbar fix
32331     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32332         document.body.scroll = "no";
32333     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32334         this.el.position('relative');
32335     }
32336     this.id = this.el.id;
32337     this.el.addClass("x-layout-container");
32338     /** false to disable window resize monitoring @type Boolean */
32339     this.monitorWindowResize = true;
32340     this.regions = {};
32341     this.addEvents({
32342         /**
32343          * @event layout
32344          * Fires when a layout is performed. 
32345          * @param {Roo.LayoutManager} this
32346          */
32347         "layout" : true,
32348         /**
32349          * @event regionresized
32350          * Fires when the user resizes a region. 
32351          * @param {Roo.LayoutRegion} region The resized region
32352          * @param {Number} newSize The new size (width for east/west, height for north/south)
32353          */
32354         "regionresized" : true,
32355         /**
32356          * @event regioncollapsed
32357          * Fires when a region is collapsed. 
32358          * @param {Roo.LayoutRegion} region The collapsed region
32359          */
32360         "regioncollapsed" : true,
32361         /**
32362          * @event regionexpanded
32363          * Fires when a region is expanded.  
32364          * @param {Roo.LayoutRegion} region The expanded region
32365          */
32366         "regionexpanded" : true
32367     });
32368     this.updating = false;
32369     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32370 };
32371
32372 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32373     /**
32374      * Returns true if this layout is currently being updated
32375      * @return {Boolean}
32376      */
32377     isUpdating : function(){
32378         return this.updating; 
32379     },
32380     
32381     /**
32382      * Suspend the LayoutManager from doing auto-layouts while
32383      * making multiple add or remove calls
32384      */
32385     beginUpdate : function(){
32386         this.updating = true;    
32387     },
32388     
32389     /**
32390      * Restore auto-layouts and optionally disable the manager from performing a layout
32391      * @param {Boolean} noLayout true to disable a layout update 
32392      */
32393     endUpdate : function(noLayout){
32394         this.updating = false;
32395         if(!noLayout){
32396             this.layout();
32397         }    
32398     },
32399     
32400     layout: function(){
32401         
32402     },
32403     
32404     onRegionResized : function(region, newSize){
32405         this.fireEvent("regionresized", region, newSize);
32406         this.layout();
32407     },
32408     
32409     onRegionCollapsed : function(region){
32410         this.fireEvent("regioncollapsed", region);
32411     },
32412     
32413     onRegionExpanded : function(region){
32414         this.fireEvent("regionexpanded", region);
32415     },
32416         
32417     /**
32418      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32419      * performs box-model adjustments.
32420      * @return {Object} The size as an object {width: (the width), height: (the height)}
32421      */
32422     getViewSize : function(){
32423         var size;
32424         if(this.el.dom != document.body){
32425             size = this.el.getSize();
32426         }else{
32427             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32428         }
32429         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32430         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32431         return size;
32432     },
32433     
32434     /**
32435      * Returns the Element this layout is bound to.
32436      * @return {Roo.Element}
32437      */
32438     getEl : function(){
32439         return this.el;
32440     },
32441     
32442     /**
32443      * Returns the specified region.
32444      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32445      * @return {Roo.LayoutRegion}
32446      */
32447     getRegion : function(target){
32448         return this.regions[target.toLowerCase()];
32449     },
32450     
32451     onWindowResize : function(){
32452         if(this.monitorWindowResize){
32453             this.layout();
32454         }
32455     }
32456 });/*
32457  * Based on:
32458  * Ext JS Library 1.1.1
32459  * Copyright(c) 2006-2007, Ext JS, LLC.
32460  *
32461  * Originally Released Under LGPL - original licence link has changed is not relivant.
32462  *
32463  * Fork - LGPL
32464  * <script type="text/javascript">
32465  */
32466 /**
32467  * @class Roo.BorderLayout
32468  * @extends Roo.LayoutManager
32469  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32470  * please see: <br><br>
32471  * <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>
32472  * <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>
32473  * Example:
32474  <pre><code>
32475  var layout = new Roo.BorderLayout(document.body, {
32476     north: {
32477         initialSize: 25,
32478         titlebar: false
32479     },
32480     west: {
32481         split:true,
32482         initialSize: 200,
32483         minSize: 175,
32484         maxSize: 400,
32485         titlebar: true,
32486         collapsible: true
32487     },
32488     east: {
32489         split:true,
32490         initialSize: 202,
32491         minSize: 175,
32492         maxSize: 400,
32493         titlebar: true,
32494         collapsible: true
32495     },
32496     south: {
32497         split:true,
32498         initialSize: 100,
32499         minSize: 100,
32500         maxSize: 200,
32501         titlebar: true,
32502         collapsible: true
32503     },
32504     center: {
32505         titlebar: true,
32506         autoScroll:true,
32507         resizeTabs: true,
32508         minTabWidth: 50,
32509         preferredTabWidth: 150
32510     }
32511 });
32512
32513 // shorthand
32514 var CP = Roo.ContentPanel;
32515
32516 layout.beginUpdate();
32517 layout.add("north", new CP("north", "North"));
32518 layout.add("south", new CP("south", {title: "South", closable: true}));
32519 layout.add("west", new CP("west", {title: "West"}));
32520 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32521 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32522 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32523 layout.getRegion("center").showPanel("center1");
32524 layout.endUpdate();
32525 </code></pre>
32526
32527 <b>The container the layout is rendered into can be either the body element or any other element.
32528 If it is not the body element, the container needs to either be an absolute positioned element,
32529 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32530 the container size if it is not the body element.</b>
32531
32532 * @constructor
32533 * Create a new BorderLayout
32534 * @param {String/HTMLElement/Element} container The container this layout is bound to
32535 * @param {Object} config Configuration options
32536  */
32537 Roo.BorderLayout = function(container, config){
32538     config = config || {};
32539     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32540     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32541     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32542         var target = this.factory.validRegions[i];
32543         if(config[target]){
32544             this.addRegion(target, config[target]);
32545         }
32546     }
32547 };
32548
32549 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32550     /**
32551      * Creates and adds a new region if it doesn't already exist.
32552      * @param {String} target The target region key (north, south, east, west or center).
32553      * @param {Object} config The regions config object
32554      * @return {BorderLayoutRegion} The new region
32555      */
32556     addRegion : function(target, config){
32557         if(!this.regions[target]){
32558             var r = this.factory.create(target, this, config);
32559             this.bindRegion(target, r);
32560         }
32561         return this.regions[target];
32562     },
32563
32564     // private (kinda)
32565     bindRegion : function(name, r){
32566         this.regions[name] = r;
32567         r.on("visibilitychange", this.layout, this);
32568         r.on("paneladded", this.layout, this);
32569         r.on("panelremoved", this.layout, this);
32570         r.on("invalidated", this.layout, this);
32571         r.on("resized", this.onRegionResized, this);
32572         r.on("collapsed", this.onRegionCollapsed, this);
32573         r.on("expanded", this.onRegionExpanded, this);
32574     },
32575
32576     /**
32577      * Performs a layout update.
32578      */
32579     layout : function(){
32580         if(this.updating) return;
32581         var size = this.getViewSize();
32582         var w = size.width;
32583         var h = size.height;
32584         var centerW = w;
32585         var centerH = h;
32586         var centerY = 0;
32587         var centerX = 0;
32588         //var x = 0, y = 0;
32589
32590         var rs = this.regions;
32591         var north = rs["north"];
32592         var south = rs["south"]; 
32593         var west = rs["west"];
32594         var east = rs["east"];
32595         var center = rs["center"];
32596         //if(this.hideOnLayout){ // not supported anymore
32597             //c.el.setStyle("display", "none");
32598         //}
32599         if(north && north.isVisible()){
32600             var b = north.getBox();
32601             var m = north.getMargins();
32602             b.width = w - (m.left+m.right);
32603             b.x = m.left;
32604             b.y = m.top;
32605             centerY = b.height + b.y + m.bottom;
32606             centerH -= centerY;
32607             north.updateBox(this.safeBox(b));
32608         }
32609         if(south && south.isVisible()){
32610             var b = south.getBox();
32611             var m = south.getMargins();
32612             b.width = w - (m.left+m.right);
32613             b.x = m.left;
32614             var totalHeight = (b.height + m.top + m.bottom);
32615             b.y = h - totalHeight + m.top;
32616             centerH -= totalHeight;
32617             south.updateBox(this.safeBox(b));
32618         }
32619         if(west && west.isVisible()){
32620             var b = west.getBox();
32621             var m = west.getMargins();
32622             b.height = centerH - (m.top+m.bottom);
32623             b.x = m.left;
32624             b.y = centerY + m.top;
32625             var totalWidth = (b.width + m.left + m.right);
32626             centerX += totalWidth;
32627             centerW -= totalWidth;
32628             west.updateBox(this.safeBox(b));
32629         }
32630         if(east && east.isVisible()){
32631             var b = east.getBox();
32632             var m = east.getMargins();
32633             b.height = centerH - (m.top+m.bottom);
32634             var totalWidth = (b.width + m.left + m.right);
32635             b.x = w - totalWidth + m.left;
32636             b.y = centerY + m.top;
32637             centerW -= totalWidth;
32638             east.updateBox(this.safeBox(b));
32639         }
32640         if(center){
32641             var m = center.getMargins();
32642             var centerBox = {
32643                 x: centerX + m.left,
32644                 y: centerY + m.top,
32645                 width: centerW - (m.left+m.right),
32646                 height: centerH - (m.top+m.bottom)
32647             };
32648             //if(this.hideOnLayout){
32649                 //center.el.setStyle("display", "block");
32650             //}
32651             center.updateBox(this.safeBox(centerBox));
32652         }
32653         this.el.repaint();
32654         this.fireEvent("layout", this);
32655     },
32656
32657     // private
32658     safeBox : function(box){
32659         box.width = Math.max(0, box.width);
32660         box.height = Math.max(0, box.height);
32661         return box;
32662     },
32663
32664     /**
32665      * Adds a ContentPanel (or subclass) to this layout.
32666      * @param {String} target The target region key (north, south, east, west or center).
32667      * @param {Roo.ContentPanel} panel The panel to add
32668      * @return {Roo.ContentPanel} The added panel
32669      */
32670     add : function(target, panel){
32671          
32672         target = target.toLowerCase();
32673         return this.regions[target].add(panel);
32674     },
32675
32676     /**
32677      * Remove a ContentPanel (or subclass) to this layout.
32678      * @param {String} target The target region key (north, south, east, west or center).
32679      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32680      * @return {Roo.ContentPanel} The removed panel
32681      */
32682     remove : function(target, panel){
32683         target = target.toLowerCase();
32684         return this.regions[target].remove(panel);
32685     },
32686
32687     /**
32688      * Searches all regions for a panel with the specified id
32689      * @param {String} panelId
32690      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32691      */
32692     findPanel : function(panelId){
32693         var rs = this.regions;
32694         for(var target in rs){
32695             if(typeof rs[target] != "function"){
32696                 var p = rs[target].getPanel(panelId);
32697                 if(p){
32698                     return p;
32699                 }
32700             }
32701         }
32702         return null;
32703     },
32704
32705     /**
32706      * Searches all regions for a panel with the specified id and activates (shows) it.
32707      * @param {String/ContentPanel} panelId The panels id or the panel itself
32708      * @return {Roo.ContentPanel} The shown panel or null
32709      */
32710     showPanel : function(panelId) {
32711       var rs = this.regions;
32712       for(var target in rs){
32713          var r = rs[target];
32714          if(typeof r != "function"){
32715             if(r.hasPanel(panelId)){
32716                return r.showPanel(panelId);
32717             }
32718          }
32719       }
32720       return null;
32721    },
32722
32723    /**
32724      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32725      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32726      */
32727     restoreState : function(provider){
32728         if(!provider){
32729             provider = Roo.state.Manager;
32730         }
32731         var sm = new Roo.LayoutStateManager();
32732         sm.init(this, provider);
32733     },
32734
32735     /**
32736      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32737      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32738      * a valid ContentPanel config object.  Example:
32739      * <pre><code>
32740 // Create the main layout
32741 var layout = new Roo.BorderLayout('main-ct', {
32742     west: {
32743         split:true,
32744         minSize: 175,
32745         titlebar: true
32746     },
32747     center: {
32748         title:'Components'
32749     }
32750 }, 'main-ct');
32751
32752 // Create and add multiple ContentPanels at once via configs
32753 layout.batchAdd({
32754    west: {
32755        id: 'source-files',
32756        autoCreate:true,
32757        title:'Ext Source Files',
32758        autoScroll:true,
32759        fitToFrame:true
32760    },
32761    center : {
32762        el: cview,
32763        autoScroll:true,
32764        fitToFrame:true,
32765        toolbar: tb,
32766        resizeEl:'cbody'
32767    }
32768 });
32769 </code></pre>
32770      * @param {Object} regions An object containing ContentPanel configs by region name
32771      */
32772     batchAdd : function(regions){
32773         this.beginUpdate();
32774         for(var rname in regions){
32775             var lr = this.regions[rname];
32776             if(lr){
32777                 this.addTypedPanels(lr, regions[rname]);
32778             }
32779         }
32780         this.endUpdate();
32781     },
32782
32783     // private
32784     addTypedPanels : function(lr, ps){
32785         if(typeof ps == 'string'){
32786             lr.add(new Roo.ContentPanel(ps));
32787         }
32788         else if(ps instanceof Array){
32789             for(var i =0, len = ps.length; i < len; i++){
32790                 this.addTypedPanels(lr, ps[i]);
32791             }
32792         }
32793         else if(!ps.events){ // raw config?
32794             var el = ps.el;
32795             delete ps.el; // prevent conflict
32796             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32797         }
32798         else {  // panel object assumed!
32799             lr.add(ps);
32800         }
32801     },
32802     /**
32803      * Adds a xtype elements to the layout.
32804      * <pre><code>
32805
32806 layout.addxtype({
32807        xtype : 'ContentPanel',
32808        region: 'west',
32809        items: [ .... ]
32810    }
32811 );
32812
32813 layout.addxtype({
32814         xtype : 'NestedLayoutPanel',
32815         region: 'west',
32816         layout: {
32817            center: { },
32818            west: { }   
32819         },
32820         items : [ ... list of content panels or nested layout panels.. ]
32821    }
32822 );
32823 </code></pre>
32824      * @param {Object} cfg Xtype definition of item to add.
32825      */
32826     addxtype : function(cfg)
32827     {
32828         // basically accepts a pannel...
32829         // can accept a layout region..!?!?
32830         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32831         
32832         if (!cfg.xtype.match(/Panel$/)) {
32833             return false;
32834         }
32835         var ret = false;
32836         
32837         if (typeof(cfg.region) == 'undefined') {
32838             Roo.log("Failed to add Panel, region was not set");
32839             Roo.log(cfg);
32840             return false;
32841         }
32842         var region = cfg.region;
32843         delete cfg.region;
32844         
32845           
32846         var xitems = [];
32847         if (cfg.items) {
32848             xitems = cfg.items;
32849             delete cfg.items;
32850         }
32851         var nb = false;
32852         
32853         switch(cfg.xtype) 
32854         {
32855             case 'ContentPanel':  // ContentPanel (el, cfg)
32856             case 'ScrollPanel':  // ContentPanel (el, cfg)
32857             case 'ViewPanel': 
32858                 if(cfg.autoCreate) {
32859                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32860                 } else {
32861                     var el = this.el.createChild();
32862                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32863                 }
32864                 
32865                 this.add(region, ret);
32866                 break;
32867             
32868             
32869             case 'TreePanel': // our new panel!
32870                 cfg.el = this.el.createChild();
32871                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32872                 this.add(region, ret);
32873                 break;
32874             
32875             case 'NestedLayoutPanel': 
32876                 // create a new Layout (which is  a Border Layout...
32877                 var el = this.el.createChild();
32878                 var clayout = cfg.layout;
32879                 delete cfg.layout;
32880                 clayout.items   = clayout.items  || [];
32881                 // replace this exitems with the clayout ones..
32882                 xitems = clayout.items;
32883                  
32884                 
32885                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32886                     cfg.background = false;
32887                 }
32888                 var layout = new Roo.BorderLayout(el, clayout);
32889                 
32890                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32891                 //console.log('adding nested layout panel '  + cfg.toSource());
32892                 this.add(region, ret);
32893                 nb = {}; /// find first...
32894                 break;
32895                 
32896             case 'GridPanel': 
32897             
32898                 // needs grid and region
32899                 
32900                 //var el = this.getRegion(region).el.createChild();
32901                 var el = this.el.createChild();
32902                 // create the grid first...
32903                 
32904                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32905                 delete cfg.grid;
32906                 if (region == 'center' && this.active ) {
32907                     cfg.background = false;
32908                 }
32909                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32910                 
32911                 this.add(region, ret);
32912                 if (cfg.background) {
32913                     ret.on('activate', function(gp) {
32914                         if (!gp.grid.rendered) {
32915                             gp.grid.render();
32916                         }
32917                     });
32918                 } else {
32919                     grid.render();
32920                 }
32921                 break;
32922            
32923            
32924            
32925                 
32926                 
32927                 
32928             default:
32929                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32930                     
32931                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32932                     this.add(region, ret);
32933                 } else {
32934                 
32935                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32936                     return null;
32937                 }
32938                 
32939              // GridPanel (grid, cfg)
32940             
32941         }
32942         this.beginUpdate();
32943         // add children..
32944         var region = '';
32945         var abn = {};
32946         Roo.each(xitems, function(i)  {
32947             region = nb && i.region ? i.region : false;
32948             
32949             var add = ret.addxtype(i);
32950            
32951             if (region) {
32952                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32953                 if (!i.background) {
32954                     abn[region] = nb[region] ;
32955                 }
32956             }
32957             
32958         });
32959         this.endUpdate();
32960
32961         // make the last non-background panel active..
32962         //if (nb) { Roo.log(abn); }
32963         if (nb) {
32964             
32965             for(var r in abn) {
32966                 region = this.getRegion(r);
32967                 if (region) {
32968                     // tried using nb[r], but it does not work..
32969                      
32970                     region.showPanel(abn[r]);
32971                    
32972                 }
32973             }
32974         }
32975         return ret;
32976         
32977     }
32978 });
32979
32980 /**
32981  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32982  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32983  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32984  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32985  * <pre><code>
32986 // shorthand
32987 var CP = Roo.ContentPanel;
32988
32989 var layout = Roo.BorderLayout.create({
32990     north: {
32991         initialSize: 25,
32992         titlebar: false,
32993         panels: [new CP("north", "North")]
32994     },
32995     west: {
32996         split:true,
32997         initialSize: 200,
32998         minSize: 175,
32999         maxSize: 400,
33000         titlebar: true,
33001         collapsible: true,
33002         panels: [new CP("west", {title: "West"})]
33003     },
33004     east: {
33005         split:true,
33006         initialSize: 202,
33007         minSize: 175,
33008         maxSize: 400,
33009         titlebar: true,
33010         collapsible: true,
33011         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33012     },
33013     south: {
33014         split:true,
33015         initialSize: 100,
33016         minSize: 100,
33017         maxSize: 200,
33018         titlebar: true,
33019         collapsible: true,
33020         panels: [new CP("south", {title: "South", closable: true})]
33021     },
33022     center: {
33023         titlebar: true,
33024         autoScroll:true,
33025         resizeTabs: true,
33026         minTabWidth: 50,
33027         preferredTabWidth: 150,
33028         panels: [
33029             new CP("center1", {title: "Close Me", closable: true}),
33030             new CP("center2", {title: "Center Panel", closable: false})
33031         ]
33032     }
33033 }, document.body);
33034
33035 layout.getRegion("center").showPanel("center1");
33036 </code></pre>
33037  * @param config
33038  * @param targetEl
33039  */
33040 Roo.BorderLayout.create = function(config, targetEl){
33041     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33042     layout.beginUpdate();
33043     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33044     for(var j = 0, jlen = regions.length; j < jlen; j++){
33045         var lr = regions[j];
33046         if(layout.regions[lr] && config[lr].panels){
33047             var r = layout.regions[lr];
33048             var ps = config[lr].panels;
33049             layout.addTypedPanels(r, ps);
33050         }
33051     }
33052     layout.endUpdate();
33053     return layout;
33054 };
33055
33056 // private
33057 Roo.BorderLayout.RegionFactory = {
33058     // private
33059     validRegions : ["north","south","east","west","center"],
33060
33061     // private
33062     create : function(target, mgr, config){
33063         target = target.toLowerCase();
33064         if(config.lightweight || config.basic){
33065             return new Roo.BasicLayoutRegion(mgr, config, target);
33066         }
33067         switch(target){
33068             case "north":
33069                 return new Roo.NorthLayoutRegion(mgr, config);
33070             case "south":
33071                 return new Roo.SouthLayoutRegion(mgr, config);
33072             case "east":
33073                 return new Roo.EastLayoutRegion(mgr, config);
33074             case "west":
33075                 return new Roo.WestLayoutRegion(mgr, config);
33076             case "center":
33077                 return new Roo.CenterLayoutRegion(mgr, config);
33078         }
33079         throw 'Layout region "'+target+'" not supported.';
33080     }
33081 };/*
33082  * Based on:
33083  * Ext JS Library 1.1.1
33084  * Copyright(c) 2006-2007, Ext JS, LLC.
33085  *
33086  * Originally Released Under LGPL - original licence link has changed is not relivant.
33087  *
33088  * Fork - LGPL
33089  * <script type="text/javascript">
33090  */
33091  
33092 /**
33093  * @class Roo.BasicLayoutRegion
33094  * @extends Roo.util.Observable
33095  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33096  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33097  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33098  */
33099 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33100     this.mgr = mgr;
33101     this.position  = pos;
33102     this.events = {
33103         /**
33104          * @scope Roo.BasicLayoutRegion
33105          */
33106         
33107         /**
33108          * @event beforeremove
33109          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33110          * @param {Roo.LayoutRegion} this
33111          * @param {Roo.ContentPanel} panel The panel
33112          * @param {Object} e The cancel event object
33113          */
33114         "beforeremove" : true,
33115         /**
33116          * @event invalidated
33117          * Fires when the layout for this region is changed.
33118          * @param {Roo.LayoutRegion} this
33119          */
33120         "invalidated" : true,
33121         /**
33122          * @event visibilitychange
33123          * Fires when this region is shown or hidden 
33124          * @param {Roo.LayoutRegion} this
33125          * @param {Boolean} visibility true or false
33126          */
33127         "visibilitychange" : true,
33128         /**
33129          * @event paneladded
33130          * Fires when a panel is added. 
33131          * @param {Roo.LayoutRegion} this
33132          * @param {Roo.ContentPanel} panel The panel
33133          */
33134         "paneladded" : true,
33135         /**
33136          * @event panelremoved
33137          * Fires when a panel is removed. 
33138          * @param {Roo.LayoutRegion} this
33139          * @param {Roo.ContentPanel} panel The panel
33140          */
33141         "panelremoved" : true,
33142         /**
33143          * @event collapsed
33144          * Fires when this region is collapsed.
33145          * @param {Roo.LayoutRegion} this
33146          */
33147         "collapsed" : true,
33148         /**
33149          * @event expanded
33150          * Fires when this region is expanded.
33151          * @param {Roo.LayoutRegion} this
33152          */
33153         "expanded" : true,
33154         /**
33155          * @event slideshow
33156          * Fires when this region is slid into view.
33157          * @param {Roo.LayoutRegion} this
33158          */
33159         "slideshow" : true,
33160         /**
33161          * @event slidehide
33162          * Fires when this region slides out of view. 
33163          * @param {Roo.LayoutRegion} this
33164          */
33165         "slidehide" : true,
33166         /**
33167          * @event panelactivated
33168          * Fires when a panel is activated. 
33169          * @param {Roo.LayoutRegion} this
33170          * @param {Roo.ContentPanel} panel The activated panel
33171          */
33172         "panelactivated" : true,
33173         /**
33174          * @event resized
33175          * Fires when the user resizes this region. 
33176          * @param {Roo.LayoutRegion} this
33177          * @param {Number} newSize The new size (width for east/west, height for north/south)
33178          */
33179         "resized" : true
33180     };
33181     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33182     this.panels = new Roo.util.MixedCollection();
33183     this.panels.getKey = this.getPanelId.createDelegate(this);
33184     this.box = null;
33185     this.activePanel = null;
33186     // ensure listeners are added...
33187     
33188     if (config.listeners || config.events) {
33189         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33190             listeners : config.listeners || {},
33191             events : config.events || {}
33192         });
33193     }
33194     
33195     if(skipConfig !== true){
33196         this.applyConfig(config);
33197     }
33198 };
33199
33200 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33201     getPanelId : function(p){
33202         return p.getId();
33203     },
33204     
33205     applyConfig : function(config){
33206         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33207         this.config = config;
33208         
33209     },
33210     
33211     /**
33212      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33213      * the width, for horizontal (north, south) the height.
33214      * @param {Number} newSize The new width or height
33215      */
33216     resizeTo : function(newSize){
33217         var el = this.el ? this.el :
33218                  (this.activePanel ? this.activePanel.getEl() : null);
33219         if(el){
33220             switch(this.position){
33221                 case "east":
33222                 case "west":
33223                     el.setWidth(newSize);
33224                     this.fireEvent("resized", this, newSize);
33225                 break;
33226                 case "north":
33227                 case "south":
33228                     el.setHeight(newSize);
33229                     this.fireEvent("resized", this, newSize);
33230                 break;                
33231             }
33232         }
33233     },
33234     
33235     getBox : function(){
33236         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33237     },
33238     
33239     getMargins : function(){
33240         return this.margins;
33241     },
33242     
33243     updateBox : function(box){
33244         this.box = box;
33245         var el = this.activePanel.getEl();
33246         el.dom.style.left = box.x + "px";
33247         el.dom.style.top = box.y + "px";
33248         this.activePanel.setSize(box.width, box.height);
33249     },
33250     
33251     /**
33252      * Returns the container element for this region.
33253      * @return {Roo.Element}
33254      */
33255     getEl : function(){
33256         return this.activePanel;
33257     },
33258     
33259     /**
33260      * Returns true if this region is currently visible.
33261      * @return {Boolean}
33262      */
33263     isVisible : function(){
33264         return this.activePanel ? true : false;
33265     },
33266     
33267     setActivePanel : function(panel){
33268         panel = this.getPanel(panel);
33269         if(this.activePanel && this.activePanel != panel){
33270             this.activePanel.setActiveState(false);
33271             this.activePanel.getEl().setLeftTop(-10000,-10000);
33272         }
33273         this.activePanel = panel;
33274         panel.setActiveState(true);
33275         if(this.box){
33276             panel.setSize(this.box.width, this.box.height);
33277         }
33278         this.fireEvent("panelactivated", this, panel);
33279         this.fireEvent("invalidated");
33280     },
33281     
33282     /**
33283      * Show the specified panel.
33284      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33285      * @return {Roo.ContentPanel} The shown panel or null
33286      */
33287     showPanel : function(panel){
33288         if(panel = this.getPanel(panel)){
33289             this.setActivePanel(panel);
33290         }
33291         return panel;
33292     },
33293     
33294     /**
33295      * Get the active panel for this region.
33296      * @return {Roo.ContentPanel} The active panel or null
33297      */
33298     getActivePanel : function(){
33299         return this.activePanel;
33300     },
33301     
33302     /**
33303      * Add the passed ContentPanel(s)
33304      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33305      * @return {Roo.ContentPanel} The panel added (if only one was added)
33306      */
33307     add : function(panel){
33308         if(arguments.length > 1){
33309             for(var i = 0, len = arguments.length; i < len; i++) {
33310                 this.add(arguments[i]);
33311             }
33312             return null;
33313         }
33314         if(this.hasPanel(panel)){
33315             this.showPanel(panel);
33316             return panel;
33317         }
33318         var el = panel.getEl();
33319         if(el.dom.parentNode != this.mgr.el.dom){
33320             this.mgr.el.dom.appendChild(el.dom);
33321         }
33322         if(panel.setRegion){
33323             panel.setRegion(this);
33324         }
33325         this.panels.add(panel);
33326         el.setStyle("position", "absolute");
33327         if(!panel.background){
33328             this.setActivePanel(panel);
33329             if(this.config.initialSize && this.panels.getCount()==1){
33330                 this.resizeTo(this.config.initialSize);
33331             }
33332         }
33333         this.fireEvent("paneladded", this, panel);
33334         return panel;
33335     },
33336     
33337     /**
33338      * Returns true if the panel is in this region.
33339      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33340      * @return {Boolean}
33341      */
33342     hasPanel : function(panel){
33343         if(typeof panel == "object"){ // must be panel obj
33344             panel = panel.getId();
33345         }
33346         return this.getPanel(panel) ? true : false;
33347     },
33348     
33349     /**
33350      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33351      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33352      * @param {Boolean} preservePanel Overrides the config preservePanel option
33353      * @return {Roo.ContentPanel} The panel that was removed
33354      */
33355     remove : function(panel, preservePanel){
33356         panel = this.getPanel(panel);
33357         if(!panel){
33358             return null;
33359         }
33360         var e = {};
33361         this.fireEvent("beforeremove", this, panel, e);
33362         if(e.cancel === true){
33363             return null;
33364         }
33365         var panelId = panel.getId();
33366         this.panels.removeKey(panelId);
33367         return panel;
33368     },
33369     
33370     /**
33371      * Returns the panel specified or null if it's not in this region.
33372      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33373      * @return {Roo.ContentPanel}
33374      */
33375     getPanel : function(id){
33376         if(typeof id == "object"){ // must be panel obj
33377             return id;
33378         }
33379         return this.panels.get(id);
33380     },
33381     
33382     /**
33383      * Returns this regions position (north/south/east/west/center).
33384      * @return {String} 
33385      */
33386     getPosition: function(){
33387         return this.position;    
33388     }
33389 });/*
33390  * Based on:
33391  * Ext JS Library 1.1.1
33392  * Copyright(c) 2006-2007, Ext JS, LLC.
33393  *
33394  * Originally Released Under LGPL - original licence link has changed is not relivant.
33395  *
33396  * Fork - LGPL
33397  * <script type="text/javascript">
33398  */
33399  
33400 /**
33401  * @class Roo.LayoutRegion
33402  * @extends Roo.BasicLayoutRegion
33403  * This class represents a region in a layout manager.
33404  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33405  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33406  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33407  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33408  * @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})
33409  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33410  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33411  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33412  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33413  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33414  * @cfg {String}    title           The title for the region (overrides panel titles)
33415  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33416  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33417  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33418  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33419  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33420  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33421  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33422  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33423  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33424  * @cfg {Boolean}   showPin         True to show a pin button
33425  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33426  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33427  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33428  * @cfg {Number}    width           For East/West panels
33429  * @cfg {Number}    height          For North/South panels
33430  * @cfg {Boolean}   split           To show the splitter
33431  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33432  */
33433 Roo.LayoutRegion = function(mgr, config, pos){
33434     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33435     var dh = Roo.DomHelper;
33436     /** This region's container element 
33437     * @type Roo.Element */
33438     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33439     /** This region's title element 
33440     * @type Roo.Element */
33441
33442     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33443         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33444         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33445     ]}, true);
33446     this.titleEl.enableDisplayMode();
33447     /** This region's title text element 
33448     * @type HTMLElement */
33449     this.titleTextEl = this.titleEl.dom.firstChild;
33450     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33451     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33452     this.closeBtn.enableDisplayMode();
33453     this.closeBtn.on("click", this.closeClicked, this);
33454     this.closeBtn.hide();
33455
33456     this.createBody(config);
33457     this.visible = true;
33458     this.collapsed = false;
33459
33460     if(config.hideWhenEmpty){
33461         this.hide();
33462         this.on("paneladded", this.validateVisibility, this);
33463         this.on("panelremoved", this.validateVisibility, this);
33464     }
33465     this.applyConfig(config);
33466 };
33467
33468 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33469
33470     createBody : function(){
33471         /** This region's body element 
33472         * @type Roo.Element */
33473         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33474     },
33475
33476     applyConfig : function(c){
33477         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33478             var dh = Roo.DomHelper;
33479             if(c.titlebar !== false){
33480                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33481                 this.collapseBtn.on("click", this.collapse, this);
33482                 this.collapseBtn.enableDisplayMode();
33483
33484                 if(c.showPin === true || this.showPin){
33485                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33486                     this.stickBtn.enableDisplayMode();
33487                     this.stickBtn.on("click", this.expand, this);
33488                     this.stickBtn.hide();
33489                 }
33490             }
33491             /** This region's collapsed element
33492             * @type Roo.Element */
33493             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33494                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33495             ]}, true);
33496             if(c.floatable !== false){
33497                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33498                this.collapsedEl.on("click", this.collapseClick, this);
33499             }
33500
33501             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33502                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33503                    id: "message", unselectable: "on", style:{"float":"left"}});
33504                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33505              }
33506             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33507             this.expandBtn.on("click", this.expand, this);
33508         }
33509         if(this.collapseBtn){
33510             this.collapseBtn.setVisible(c.collapsible == true);
33511         }
33512         this.cmargins = c.cmargins || this.cmargins ||
33513                          (this.position == "west" || this.position == "east" ?
33514                              {top: 0, left: 2, right:2, bottom: 0} :
33515                              {top: 2, left: 0, right:0, bottom: 2});
33516         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33517         this.bottomTabs = c.tabPosition != "top";
33518         this.autoScroll = c.autoScroll || false;
33519         if(this.autoScroll){
33520             this.bodyEl.setStyle("overflow", "auto");
33521         }else{
33522             this.bodyEl.setStyle("overflow", "hidden");
33523         }
33524         //if(c.titlebar !== false){
33525             if((!c.titlebar && !c.title) || c.titlebar === false){
33526                 this.titleEl.hide();
33527             }else{
33528                 this.titleEl.show();
33529                 if(c.title){
33530                     this.titleTextEl.innerHTML = c.title;
33531                 }
33532             }
33533         //}
33534         this.duration = c.duration || .30;
33535         this.slideDuration = c.slideDuration || .45;
33536         this.config = c;
33537         if(c.collapsed){
33538             this.collapse(true);
33539         }
33540         if(c.hidden){
33541             this.hide();
33542         }
33543     },
33544     /**
33545      * Returns true if this region is currently visible.
33546      * @return {Boolean}
33547      */
33548     isVisible : function(){
33549         return this.visible;
33550     },
33551
33552     /**
33553      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33554      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33555      */
33556     setCollapsedTitle : function(title){
33557         title = title || "&#160;";
33558         if(this.collapsedTitleTextEl){
33559             this.collapsedTitleTextEl.innerHTML = title;
33560         }
33561     },
33562
33563     getBox : function(){
33564         var b;
33565         if(!this.collapsed){
33566             b = this.el.getBox(false, true);
33567         }else{
33568             b = this.collapsedEl.getBox(false, true);
33569         }
33570         return b;
33571     },
33572
33573     getMargins : function(){
33574         return this.collapsed ? this.cmargins : this.margins;
33575     },
33576
33577     highlight : function(){
33578         this.el.addClass("x-layout-panel-dragover");
33579     },
33580
33581     unhighlight : function(){
33582         this.el.removeClass("x-layout-panel-dragover");
33583     },
33584
33585     updateBox : function(box){
33586         this.box = box;
33587         if(!this.collapsed){
33588             this.el.dom.style.left = box.x + "px";
33589             this.el.dom.style.top = box.y + "px";
33590             this.updateBody(box.width, box.height);
33591         }else{
33592             this.collapsedEl.dom.style.left = box.x + "px";
33593             this.collapsedEl.dom.style.top = box.y + "px";
33594             this.collapsedEl.setSize(box.width, box.height);
33595         }
33596         if(this.tabs){
33597             this.tabs.autoSizeTabs();
33598         }
33599     },
33600
33601     updateBody : function(w, h){
33602         if(w !== null){
33603             this.el.setWidth(w);
33604             w -= this.el.getBorderWidth("rl");
33605             if(this.config.adjustments){
33606                 w += this.config.adjustments[0];
33607             }
33608         }
33609         if(h !== null){
33610             this.el.setHeight(h);
33611             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33612             h -= this.el.getBorderWidth("tb");
33613             if(this.config.adjustments){
33614                 h += this.config.adjustments[1];
33615             }
33616             this.bodyEl.setHeight(h);
33617             if(this.tabs){
33618                 h = this.tabs.syncHeight(h);
33619             }
33620         }
33621         if(this.panelSize){
33622             w = w !== null ? w : this.panelSize.width;
33623             h = h !== null ? h : this.panelSize.height;
33624         }
33625         if(this.activePanel){
33626             var el = this.activePanel.getEl();
33627             w = w !== null ? w : el.getWidth();
33628             h = h !== null ? h : el.getHeight();
33629             this.panelSize = {width: w, height: h};
33630             this.activePanel.setSize(w, h);
33631         }
33632         if(Roo.isIE && this.tabs){
33633             this.tabs.el.repaint();
33634         }
33635     },
33636
33637     /**
33638      * Returns the container element for this region.
33639      * @return {Roo.Element}
33640      */
33641     getEl : function(){
33642         return this.el;
33643     },
33644
33645     /**
33646      * Hides this region.
33647      */
33648     hide : function(){
33649         if(!this.collapsed){
33650             this.el.dom.style.left = "-2000px";
33651             this.el.hide();
33652         }else{
33653             this.collapsedEl.dom.style.left = "-2000px";
33654             this.collapsedEl.hide();
33655         }
33656         this.visible = false;
33657         this.fireEvent("visibilitychange", this, false);
33658     },
33659
33660     /**
33661      * Shows this region if it was previously hidden.
33662      */
33663     show : function(){
33664         if(!this.collapsed){
33665             this.el.show();
33666         }else{
33667             this.collapsedEl.show();
33668         }
33669         this.visible = true;
33670         this.fireEvent("visibilitychange", this, true);
33671     },
33672
33673     closeClicked : function(){
33674         if(this.activePanel){
33675             this.remove(this.activePanel);
33676         }
33677     },
33678
33679     collapseClick : function(e){
33680         if(this.isSlid){
33681            e.stopPropagation();
33682            this.slideIn();
33683         }else{
33684            e.stopPropagation();
33685            this.slideOut();
33686         }
33687     },
33688
33689     /**
33690      * Collapses this region.
33691      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33692      */
33693     collapse : function(skipAnim){
33694         if(this.collapsed) return;
33695         this.collapsed = true;
33696         if(this.split){
33697             this.split.el.hide();
33698         }
33699         if(this.config.animate && skipAnim !== true){
33700             this.fireEvent("invalidated", this);
33701             this.animateCollapse();
33702         }else{
33703             this.el.setLocation(-20000,-20000);
33704             this.el.hide();
33705             this.collapsedEl.show();
33706             this.fireEvent("collapsed", this);
33707             this.fireEvent("invalidated", this);
33708         }
33709     },
33710
33711     animateCollapse : function(){
33712         // overridden
33713     },
33714
33715     /**
33716      * Expands this region if it was previously collapsed.
33717      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33718      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33719      */
33720     expand : function(e, skipAnim){
33721         if(e) e.stopPropagation();
33722         if(!this.collapsed || this.el.hasActiveFx()) return;
33723         if(this.isSlid){
33724             this.afterSlideIn();
33725             skipAnim = true;
33726         }
33727         this.collapsed = false;
33728         if(this.config.animate && skipAnim !== true){
33729             this.animateExpand();
33730         }else{
33731             this.el.show();
33732             if(this.split){
33733                 this.split.el.show();
33734             }
33735             this.collapsedEl.setLocation(-2000,-2000);
33736             this.collapsedEl.hide();
33737             this.fireEvent("invalidated", this);
33738             this.fireEvent("expanded", this);
33739         }
33740     },
33741
33742     animateExpand : function(){
33743         // overridden
33744     },
33745
33746     initTabs : function()
33747     {
33748         this.bodyEl.setStyle("overflow", "hidden");
33749         var ts = new Roo.TabPanel(
33750                 this.bodyEl.dom,
33751                 {
33752                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33753                     disableTooltips: this.config.disableTabTips,
33754                     toolbar : this.config.toolbar
33755                 }
33756         );
33757         if(this.config.hideTabs){
33758             ts.stripWrap.setDisplayed(false);
33759         }
33760         this.tabs = ts;
33761         ts.resizeTabs = this.config.resizeTabs === true;
33762         ts.minTabWidth = this.config.minTabWidth || 40;
33763         ts.maxTabWidth = this.config.maxTabWidth || 250;
33764         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33765         ts.monitorResize = false;
33766         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33767         ts.bodyEl.addClass('x-layout-tabs-body');
33768         this.panels.each(this.initPanelAsTab, this);
33769     },
33770
33771     initPanelAsTab : function(panel){
33772         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33773                     this.config.closeOnTab && panel.isClosable());
33774         if(panel.tabTip !== undefined){
33775             ti.setTooltip(panel.tabTip);
33776         }
33777         ti.on("activate", function(){
33778               this.setActivePanel(panel);
33779         }, this);
33780         if(this.config.closeOnTab){
33781             ti.on("beforeclose", function(t, e){
33782                 e.cancel = true;
33783                 this.remove(panel);
33784             }, this);
33785         }
33786         return ti;
33787     },
33788
33789     updatePanelTitle : function(panel, title){
33790         if(this.activePanel == panel){
33791             this.updateTitle(title);
33792         }
33793         if(this.tabs){
33794             var ti = this.tabs.getTab(panel.getEl().id);
33795             ti.setText(title);
33796             if(panel.tabTip !== undefined){
33797                 ti.setTooltip(panel.tabTip);
33798             }
33799         }
33800     },
33801
33802     updateTitle : function(title){
33803         if(this.titleTextEl && !this.config.title){
33804             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33805         }
33806     },
33807
33808     setActivePanel : function(panel){
33809         panel = this.getPanel(panel);
33810         if(this.activePanel && this.activePanel != panel){
33811             this.activePanel.setActiveState(false);
33812         }
33813         this.activePanel = panel;
33814         panel.setActiveState(true);
33815         if(this.panelSize){
33816             panel.setSize(this.panelSize.width, this.panelSize.height);
33817         }
33818         if(this.closeBtn){
33819             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33820         }
33821         this.updateTitle(panel.getTitle());
33822         if(this.tabs){
33823             this.fireEvent("invalidated", this);
33824         }
33825         this.fireEvent("panelactivated", this, panel);
33826     },
33827
33828     /**
33829      * Shows the specified panel.
33830      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33831      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33832      */
33833     showPanel : function(panel){
33834         if(panel = this.getPanel(panel)){
33835             if(this.tabs){
33836                 var tab = this.tabs.getTab(panel.getEl().id);
33837                 if(tab.isHidden()){
33838                     this.tabs.unhideTab(tab.id);
33839                 }
33840                 tab.activate();
33841             }else{
33842                 this.setActivePanel(panel);
33843             }
33844         }
33845         return panel;
33846     },
33847
33848     /**
33849      * Get the active panel for this region.
33850      * @return {Roo.ContentPanel} The active panel or null
33851      */
33852     getActivePanel : function(){
33853         return this.activePanel;
33854     },
33855
33856     validateVisibility : function(){
33857         if(this.panels.getCount() < 1){
33858             this.updateTitle("&#160;");
33859             this.closeBtn.hide();
33860             this.hide();
33861         }else{
33862             if(!this.isVisible()){
33863                 this.show();
33864             }
33865         }
33866     },
33867
33868     /**
33869      * Adds the passed ContentPanel(s) to this region.
33870      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33871      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33872      */
33873     add : function(panel){
33874         if(arguments.length > 1){
33875             for(var i = 0, len = arguments.length; i < len; i++) {
33876                 this.add(arguments[i]);
33877             }
33878             return null;
33879         }
33880         if(this.hasPanel(panel)){
33881             this.showPanel(panel);
33882             return panel;
33883         }
33884         panel.setRegion(this);
33885         this.panels.add(panel);
33886         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33887             this.bodyEl.dom.appendChild(panel.getEl().dom);
33888             if(panel.background !== true){
33889                 this.setActivePanel(panel);
33890             }
33891             this.fireEvent("paneladded", this, panel);
33892             return panel;
33893         }
33894         if(!this.tabs){
33895             this.initTabs();
33896         }else{
33897             this.initPanelAsTab(panel);
33898         }
33899         if(panel.background !== true){
33900             this.tabs.activate(panel.getEl().id);
33901         }
33902         this.fireEvent("paneladded", this, panel);
33903         return panel;
33904     },
33905
33906     /**
33907      * Hides the tab for the specified panel.
33908      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33909      */
33910     hidePanel : function(panel){
33911         if(this.tabs && (panel = this.getPanel(panel))){
33912             this.tabs.hideTab(panel.getEl().id);
33913         }
33914     },
33915
33916     /**
33917      * Unhides the tab for a previously hidden panel.
33918      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33919      */
33920     unhidePanel : function(panel){
33921         if(this.tabs && (panel = this.getPanel(panel))){
33922             this.tabs.unhideTab(panel.getEl().id);
33923         }
33924     },
33925
33926     clearPanels : function(){
33927         while(this.panels.getCount() > 0){
33928              this.remove(this.panels.first());
33929         }
33930     },
33931
33932     /**
33933      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33934      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33935      * @param {Boolean} preservePanel Overrides the config preservePanel option
33936      * @return {Roo.ContentPanel} The panel that was removed
33937      */
33938     remove : function(panel, preservePanel){
33939         panel = this.getPanel(panel);
33940         if(!panel){
33941             return null;
33942         }
33943         var e = {};
33944         this.fireEvent("beforeremove", this, panel, e);
33945         if(e.cancel === true){
33946             return null;
33947         }
33948         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33949         var panelId = panel.getId();
33950         this.panels.removeKey(panelId);
33951         if(preservePanel){
33952             document.body.appendChild(panel.getEl().dom);
33953         }
33954         if(this.tabs){
33955             this.tabs.removeTab(panel.getEl().id);
33956         }else if (!preservePanel){
33957             this.bodyEl.dom.removeChild(panel.getEl().dom);
33958         }
33959         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33960             var p = this.panels.first();
33961             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33962             tempEl.appendChild(p.getEl().dom);
33963             this.bodyEl.update("");
33964             this.bodyEl.dom.appendChild(p.getEl().dom);
33965             tempEl = null;
33966             this.updateTitle(p.getTitle());
33967             this.tabs = null;
33968             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33969             this.setActivePanel(p);
33970         }
33971         panel.setRegion(null);
33972         if(this.activePanel == panel){
33973             this.activePanel = null;
33974         }
33975         if(this.config.autoDestroy !== false && preservePanel !== true){
33976             try{panel.destroy();}catch(e){}
33977         }
33978         this.fireEvent("panelremoved", this, panel);
33979         return panel;
33980     },
33981
33982     /**
33983      * Returns the TabPanel component used by this region
33984      * @return {Roo.TabPanel}
33985      */
33986     getTabs : function(){
33987         return this.tabs;
33988     },
33989
33990     createTool : function(parentEl, className){
33991         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33992             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33993         btn.addClassOnOver("x-layout-tools-button-over");
33994         return btn;
33995     }
33996 });/*
33997  * Based on:
33998  * Ext JS Library 1.1.1
33999  * Copyright(c) 2006-2007, Ext JS, LLC.
34000  *
34001  * Originally Released Under LGPL - original licence link has changed is not relivant.
34002  *
34003  * Fork - LGPL
34004  * <script type="text/javascript">
34005  */
34006  
34007
34008
34009 /**
34010  * @class Roo.SplitLayoutRegion
34011  * @extends Roo.LayoutRegion
34012  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34013  */
34014 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34015     this.cursor = cursor;
34016     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34017 };
34018
34019 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34020     splitTip : "Drag to resize.",
34021     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34022     useSplitTips : false,
34023
34024     applyConfig : function(config){
34025         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34026         if(config.split){
34027             if(!this.split){
34028                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34029                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34030                 /** The SplitBar for this region 
34031                 * @type Roo.SplitBar */
34032                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34033                 this.split.on("moved", this.onSplitMove, this);
34034                 this.split.useShim = config.useShim === true;
34035                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34036                 if(this.useSplitTips){
34037                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34038                 }
34039                 if(config.collapsible){
34040                     this.split.el.on("dblclick", this.collapse,  this);
34041                 }
34042             }
34043             if(typeof config.minSize != "undefined"){
34044                 this.split.minSize = config.minSize;
34045             }
34046             if(typeof config.maxSize != "undefined"){
34047                 this.split.maxSize = config.maxSize;
34048             }
34049             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34050                 this.hideSplitter();
34051             }
34052         }
34053     },
34054
34055     getHMaxSize : function(){
34056          var cmax = this.config.maxSize || 10000;
34057          var center = this.mgr.getRegion("center");
34058          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34059     },
34060
34061     getVMaxSize : function(){
34062          var cmax = this.config.maxSize || 10000;
34063          var center = this.mgr.getRegion("center");
34064          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34065     },
34066
34067     onSplitMove : function(split, newSize){
34068         this.fireEvent("resized", this, newSize);
34069     },
34070     
34071     /** 
34072      * Returns the {@link Roo.SplitBar} for this region.
34073      * @return {Roo.SplitBar}
34074      */
34075     getSplitBar : function(){
34076         return this.split;
34077     },
34078     
34079     hide : function(){
34080         this.hideSplitter();
34081         Roo.SplitLayoutRegion.superclass.hide.call(this);
34082     },
34083
34084     hideSplitter : function(){
34085         if(this.split){
34086             this.split.el.setLocation(-2000,-2000);
34087             this.split.el.hide();
34088         }
34089     },
34090
34091     show : function(){
34092         if(this.split){
34093             this.split.el.show();
34094         }
34095         Roo.SplitLayoutRegion.superclass.show.call(this);
34096     },
34097     
34098     beforeSlide: function(){
34099         if(Roo.isGecko){// firefox overflow auto bug workaround
34100             this.bodyEl.clip();
34101             if(this.tabs) this.tabs.bodyEl.clip();
34102             if(this.activePanel){
34103                 this.activePanel.getEl().clip();
34104                 
34105                 if(this.activePanel.beforeSlide){
34106                     this.activePanel.beforeSlide();
34107                 }
34108             }
34109         }
34110     },
34111     
34112     afterSlide : function(){
34113         if(Roo.isGecko){// firefox overflow auto bug workaround
34114             this.bodyEl.unclip();
34115             if(this.tabs) this.tabs.bodyEl.unclip();
34116             if(this.activePanel){
34117                 this.activePanel.getEl().unclip();
34118                 if(this.activePanel.afterSlide){
34119                     this.activePanel.afterSlide();
34120                 }
34121             }
34122         }
34123     },
34124
34125     initAutoHide : function(){
34126         if(this.autoHide !== false){
34127             if(!this.autoHideHd){
34128                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34129                 this.autoHideHd = {
34130                     "mouseout": function(e){
34131                         if(!e.within(this.el, true)){
34132                             st.delay(500);
34133                         }
34134                     },
34135                     "mouseover" : function(e){
34136                         st.cancel();
34137                     },
34138                     scope : this
34139                 };
34140             }
34141             this.el.on(this.autoHideHd);
34142         }
34143     },
34144
34145     clearAutoHide : function(){
34146         if(this.autoHide !== false){
34147             this.el.un("mouseout", this.autoHideHd.mouseout);
34148             this.el.un("mouseover", this.autoHideHd.mouseover);
34149         }
34150     },
34151
34152     clearMonitor : function(){
34153         Roo.get(document).un("click", this.slideInIf, this);
34154     },
34155
34156     // these names are backwards but not changed for compat
34157     slideOut : function(){
34158         if(this.isSlid || this.el.hasActiveFx()){
34159             return;
34160         }
34161         this.isSlid = true;
34162         if(this.collapseBtn){
34163             this.collapseBtn.hide();
34164         }
34165         this.closeBtnState = this.closeBtn.getStyle('display');
34166         this.closeBtn.hide();
34167         if(this.stickBtn){
34168             this.stickBtn.show();
34169         }
34170         this.el.show();
34171         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34172         this.beforeSlide();
34173         this.el.setStyle("z-index", 10001);
34174         this.el.slideIn(this.getSlideAnchor(), {
34175             callback: function(){
34176                 this.afterSlide();
34177                 this.initAutoHide();
34178                 Roo.get(document).on("click", this.slideInIf, this);
34179                 this.fireEvent("slideshow", this);
34180             },
34181             scope: this,
34182             block: true
34183         });
34184     },
34185
34186     afterSlideIn : function(){
34187         this.clearAutoHide();
34188         this.isSlid = false;
34189         this.clearMonitor();
34190         this.el.setStyle("z-index", "");
34191         if(this.collapseBtn){
34192             this.collapseBtn.show();
34193         }
34194         this.closeBtn.setStyle('display', this.closeBtnState);
34195         if(this.stickBtn){
34196             this.stickBtn.hide();
34197         }
34198         this.fireEvent("slidehide", this);
34199     },
34200
34201     slideIn : function(cb){
34202         if(!this.isSlid || this.el.hasActiveFx()){
34203             Roo.callback(cb);
34204             return;
34205         }
34206         this.isSlid = false;
34207         this.beforeSlide();
34208         this.el.slideOut(this.getSlideAnchor(), {
34209             callback: function(){
34210                 this.el.setLeftTop(-10000, -10000);
34211                 this.afterSlide();
34212                 this.afterSlideIn();
34213                 Roo.callback(cb);
34214             },
34215             scope: this,
34216             block: true
34217         });
34218     },
34219     
34220     slideInIf : function(e){
34221         if(!e.within(this.el)){
34222             this.slideIn();
34223         }
34224     },
34225
34226     animateCollapse : function(){
34227         this.beforeSlide();
34228         this.el.setStyle("z-index", 20000);
34229         var anchor = this.getSlideAnchor();
34230         this.el.slideOut(anchor, {
34231             callback : function(){
34232                 this.el.setStyle("z-index", "");
34233                 this.collapsedEl.slideIn(anchor, {duration:.3});
34234                 this.afterSlide();
34235                 this.el.setLocation(-10000,-10000);
34236                 this.el.hide();
34237                 this.fireEvent("collapsed", this);
34238             },
34239             scope: this,
34240             block: true
34241         });
34242     },
34243
34244     animateExpand : function(){
34245         this.beforeSlide();
34246         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34247         this.el.setStyle("z-index", 20000);
34248         this.collapsedEl.hide({
34249             duration:.1
34250         });
34251         this.el.slideIn(this.getSlideAnchor(), {
34252             callback : function(){
34253                 this.el.setStyle("z-index", "");
34254                 this.afterSlide();
34255                 if(this.split){
34256                     this.split.el.show();
34257                 }
34258                 this.fireEvent("invalidated", this);
34259                 this.fireEvent("expanded", this);
34260             },
34261             scope: this,
34262             block: true
34263         });
34264     },
34265
34266     anchors : {
34267         "west" : "left",
34268         "east" : "right",
34269         "north" : "top",
34270         "south" : "bottom"
34271     },
34272
34273     sanchors : {
34274         "west" : "l",
34275         "east" : "r",
34276         "north" : "t",
34277         "south" : "b"
34278     },
34279
34280     canchors : {
34281         "west" : "tl-tr",
34282         "east" : "tr-tl",
34283         "north" : "tl-bl",
34284         "south" : "bl-tl"
34285     },
34286
34287     getAnchor : function(){
34288         return this.anchors[this.position];
34289     },
34290
34291     getCollapseAnchor : function(){
34292         return this.canchors[this.position];
34293     },
34294
34295     getSlideAnchor : function(){
34296         return this.sanchors[this.position];
34297     },
34298
34299     getAlignAdj : function(){
34300         var cm = this.cmargins;
34301         switch(this.position){
34302             case "west":
34303                 return [0, 0];
34304             break;
34305             case "east":
34306                 return [0, 0];
34307             break;
34308             case "north":
34309                 return [0, 0];
34310             break;
34311             case "south":
34312                 return [0, 0];
34313             break;
34314         }
34315     },
34316
34317     getExpandAdj : function(){
34318         var c = this.collapsedEl, cm = this.cmargins;
34319         switch(this.position){
34320             case "west":
34321                 return [-(cm.right+c.getWidth()+cm.left), 0];
34322             break;
34323             case "east":
34324                 return [cm.right+c.getWidth()+cm.left, 0];
34325             break;
34326             case "north":
34327                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34328             break;
34329             case "south":
34330                 return [0, cm.top+cm.bottom+c.getHeight()];
34331             break;
34332         }
34333     }
34334 });/*
34335  * Based on:
34336  * Ext JS Library 1.1.1
34337  * Copyright(c) 2006-2007, Ext JS, LLC.
34338  *
34339  * Originally Released Under LGPL - original licence link has changed is not relivant.
34340  *
34341  * Fork - LGPL
34342  * <script type="text/javascript">
34343  */
34344 /*
34345  * These classes are private internal classes
34346  */
34347 Roo.CenterLayoutRegion = function(mgr, config){
34348     Roo.LayoutRegion.call(this, mgr, config, "center");
34349     this.visible = true;
34350     this.minWidth = config.minWidth || 20;
34351     this.minHeight = config.minHeight || 20;
34352 };
34353
34354 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34355     hide : function(){
34356         // center panel can't be hidden
34357     },
34358     
34359     show : function(){
34360         // center panel can't be hidden
34361     },
34362     
34363     getMinWidth: function(){
34364         return this.minWidth;
34365     },
34366     
34367     getMinHeight: function(){
34368         return this.minHeight;
34369     }
34370 });
34371
34372
34373 Roo.NorthLayoutRegion = function(mgr, config){
34374     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34375     if(this.split){
34376         this.split.placement = Roo.SplitBar.TOP;
34377         this.split.orientation = Roo.SplitBar.VERTICAL;
34378         this.split.el.addClass("x-layout-split-v");
34379     }
34380     var size = config.initialSize || config.height;
34381     if(typeof size != "undefined"){
34382         this.el.setHeight(size);
34383     }
34384 };
34385 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34386     orientation: Roo.SplitBar.VERTICAL,
34387     getBox : function(){
34388         if(this.collapsed){
34389             return this.collapsedEl.getBox();
34390         }
34391         var box = this.el.getBox();
34392         if(this.split){
34393             box.height += this.split.el.getHeight();
34394         }
34395         return box;
34396     },
34397     
34398     updateBox : function(box){
34399         if(this.split && !this.collapsed){
34400             box.height -= this.split.el.getHeight();
34401             this.split.el.setLeft(box.x);
34402             this.split.el.setTop(box.y+box.height);
34403             this.split.el.setWidth(box.width);
34404         }
34405         if(this.collapsed){
34406             this.updateBody(box.width, null);
34407         }
34408         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34409     }
34410 });
34411
34412 Roo.SouthLayoutRegion = function(mgr, config){
34413     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34414     if(this.split){
34415         this.split.placement = Roo.SplitBar.BOTTOM;
34416         this.split.orientation = Roo.SplitBar.VERTICAL;
34417         this.split.el.addClass("x-layout-split-v");
34418     }
34419     var size = config.initialSize || config.height;
34420     if(typeof size != "undefined"){
34421         this.el.setHeight(size);
34422     }
34423 };
34424 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34425     orientation: Roo.SplitBar.VERTICAL,
34426     getBox : function(){
34427         if(this.collapsed){
34428             return this.collapsedEl.getBox();
34429         }
34430         var box = this.el.getBox();
34431         if(this.split){
34432             var sh = this.split.el.getHeight();
34433             box.height += sh;
34434             box.y -= sh;
34435         }
34436         return box;
34437     },
34438     
34439     updateBox : function(box){
34440         if(this.split && !this.collapsed){
34441             var sh = this.split.el.getHeight();
34442             box.height -= sh;
34443             box.y += sh;
34444             this.split.el.setLeft(box.x);
34445             this.split.el.setTop(box.y-sh);
34446             this.split.el.setWidth(box.width);
34447         }
34448         if(this.collapsed){
34449             this.updateBody(box.width, null);
34450         }
34451         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34452     }
34453 });
34454
34455 Roo.EastLayoutRegion = function(mgr, config){
34456     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34457     if(this.split){
34458         this.split.placement = Roo.SplitBar.RIGHT;
34459         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34460         this.split.el.addClass("x-layout-split-h");
34461     }
34462     var size = config.initialSize || config.width;
34463     if(typeof size != "undefined"){
34464         this.el.setWidth(size);
34465     }
34466 };
34467 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34468     orientation: Roo.SplitBar.HORIZONTAL,
34469     getBox : function(){
34470         if(this.collapsed){
34471             return this.collapsedEl.getBox();
34472         }
34473         var box = this.el.getBox();
34474         if(this.split){
34475             var sw = this.split.el.getWidth();
34476             box.width += sw;
34477             box.x -= sw;
34478         }
34479         return box;
34480     },
34481
34482     updateBox : function(box){
34483         if(this.split && !this.collapsed){
34484             var sw = this.split.el.getWidth();
34485             box.width -= sw;
34486             this.split.el.setLeft(box.x);
34487             this.split.el.setTop(box.y);
34488             this.split.el.setHeight(box.height);
34489             box.x += sw;
34490         }
34491         if(this.collapsed){
34492             this.updateBody(null, box.height);
34493         }
34494         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34495     }
34496 });
34497
34498 Roo.WestLayoutRegion = function(mgr, config){
34499     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34500     if(this.split){
34501         this.split.placement = Roo.SplitBar.LEFT;
34502         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34503         this.split.el.addClass("x-layout-split-h");
34504     }
34505     var size = config.initialSize || config.width;
34506     if(typeof size != "undefined"){
34507         this.el.setWidth(size);
34508     }
34509 };
34510 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34511     orientation: Roo.SplitBar.HORIZONTAL,
34512     getBox : function(){
34513         if(this.collapsed){
34514             return this.collapsedEl.getBox();
34515         }
34516         var box = this.el.getBox();
34517         if(this.split){
34518             box.width += this.split.el.getWidth();
34519         }
34520         return box;
34521     },
34522     
34523     updateBox : function(box){
34524         if(this.split && !this.collapsed){
34525             var sw = this.split.el.getWidth();
34526             box.width -= sw;
34527             this.split.el.setLeft(box.x+box.width);
34528             this.split.el.setTop(box.y);
34529             this.split.el.setHeight(box.height);
34530         }
34531         if(this.collapsed){
34532             this.updateBody(null, box.height);
34533         }
34534         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34535     }
34536 });
34537 /*
34538  * Based on:
34539  * Ext JS Library 1.1.1
34540  * Copyright(c) 2006-2007, Ext JS, LLC.
34541  *
34542  * Originally Released Under LGPL - original licence link has changed is not relivant.
34543  *
34544  * Fork - LGPL
34545  * <script type="text/javascript">
34546  */
34547  
34548  
34549 /*
34550  * Private internal class for reading and applying state
34551  */
34552 Roo.LayoutStateManager = function(layout){
34553      // default empty state
34554      this.state = {
34555         north: {},
34556         south: {},
34557         east: {},
34558         west: {}       
34559     };
34560 };
34561
34562 Roo.LayoutStateManager.prototype = {
34563     init : function(layout, provider){
34564         this.provider = provider;
34565         var state = provider.get(layout.id+"-layout-state");
34566         if(state){
34567             var wasUpdating = layout.isUpdating();
34568             if(!wasUpdating){
34569                 layout.beginUpdate();
34570             }
34571             for(var key in state){
34572                 if(typeof state[key] != "function"){
34573                     var rstate = state[key];
34574                     var r = layout.getRegion(key);
34575                     if(r && rstate){
34576                         if(rstate.size){
34577                             r.resizeTo(rstate.size);
34578                         }
34579                         if(rstate.collapsed == true){
34580                             r.collapse(true);
34581                         }else{
34582                             r.expand(null, true);
34583                         }
34584                     }
34585                 }
34586             }
34587             if(!wasUpdating){
34588                 layout.endUpdate();
34589             }
34590             this.state = state; 
34591         }
34592         this.layout = layout;
34593         layout.on("regionresized", this.onRegionResized, this);
34594         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34595         layout.on("regionexpanded", this.onRegionExpanded, this);
34596     },
34597     
34598     storeState : function(){
34599         this.provider.set(this.layout.id+"-layout-state", this.state);
34600     },
34601     
34602     onRegionResized : function(region, newSize){
34603         this.state[region.getPosition()].size = newSize;
34604         this.storeState();
34605     },
34606     
34607     onRegionCollapsed : function(region){
34608         this.state[region.getPosition()].collapsed = true;
34609         this.storeState();
34610     },
34611     
34612     onRegionExpanded : function(region){
34613         this.state[region.getPosition()].collapsed = false;
34614         this.storeState();
34615     }
34616 };/*
34617  * Based on:
34618  * Ext JS Library 1.1.1
34619  * Copyright(c) 2006-2007, Ext JS, LLC.
34620  *
34621  * Originally Released Under LGPL - original licence link has changed is not relivant.
34622  *
34623  * Fork - LGPL
34624  * <script type="text/javascript">
34625  */
34626 /**
34627  * @class Roo.ContentPanel
34628  * @extends Roo.util.Observable
34629  * A basic ContentPanel element.
34630  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34631  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34632  * @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
34633  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34634  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34635  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34636  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34637  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34638  * @cfg {String} title          The title for this panel
34639  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34640  * @cfg {String} url            Calls {@link #setUrl} with this value
34641  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34642  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34643  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34644  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34645
34646  * @constructor
34647  * Create a new ContentPanel.
34648  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34649  * @param {String/Object} config A string to set only the title or a config object
34650  * @param {String} content (optional) Set the HTML content for this panel
34651  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34652  */
34653 Roo.ContentPanel = function(el, config, content){
34654     
34655      
34656     /*
34657     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34658         config = el;
34659         el = Roo.id();
34660     }
34661     if (config && config.parentLayout) { 
34662         el = config.parentLayout.el.createChild(); 
34663     }
34664     */
34665     if(el.autoCreate){ // xtype is available if this is called from factory
34666         config = el;
34667         el = Roo.id();
34668     }
34669     this.el = Roo.get(el);
34670     if(!this.el && config && config.autoCreate){
34671         if(typeof config.autoCreate == "object"){
34672             if(!config.autoCreate.id){
34673                 config.autoCreate.id = config.id||el;
34674             }
34675             this.el = Roo.DomHelper.append(document.body,
34676                         config.autoCreate, true);
34677         }else{
34678             this.el = Roo.DomHelper.append(document.body,
34679                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34680         }
34681     }
34682     this.closable = false;
34683     this.loaded = false;
34684     this.active = false;
34685     if(typeof config == "string"){
34686         this.title = config;
34687     }else{
34688         Roo.apply(this, config);
34689     }
34690     
34691     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34692         this.wrapEl = this.el.wrap();
34693         this.toolbar.container = this.el.insertSibling(false, 'before');
34694         this.toolbar = new Roo.Toolbar(this.toolbar);
34695     }
34696     
34697     // xtype created footer. - not sure if will work as we normally have to render first..
34698     if (this.footer && !this.footer.el && this.footer.xtype) {
34699         if (!this.wrapEl) {
34700             this.wrapEl = this.el.wrap();
34701         }
34702     
34703         this.footer.container = this.wrapEl.createChild();
34704          
34705         this.footer = Roo.factory(this.footer, Roo);
34706         
34707     }
34708     
34709     if(this.resizeEl){
34710         this.resizeEl = Roo.get(this.resizeEl, true);
34711     }else{
34712         this.resizeEl = this.el;
34713     }
34714     // handle view.xtype
34715     
34716  
34717     
34718     
34719     this.addEvents({
34720         /**
34721          * @event activate
34722          * Fires when this panel is activated. 
34723          * @param {Roo.ContentPanel} this
34724          */
34725         "activate" : true,
34726         /**
34727          * @event deactivate
34728          * Fires when this panel is activated. 
34729          * @param {Roo.ContentPanel} this
34730          */
34731         "deactivate" : true,
34732
34733         /**
34734          * @event resize
34735          * Fires when this panel is resized if fitToFrame is true.
34736          * @param {Roo.ContentPanel} this
34737          * @param {Number} width The width after any component adjustments
34738          * @param {Number} height The height after any component adjustments
34739          */
34740         "resize" : true,
34741         
34742          /**
34743          * @event render
34744          * Fires when this tab is created
34745          * @param {Roo.ContentPanel} this
34746          */
34747         "render" : true
34748         
34749         
34750         
34751     });
34752     
34753
34754     
34755     
34756     if(this.autoScroll){
34757         this.resizeEl.setStyle("overflow", "auto");
34758     } else {
34759         // fix randome scrolling
34760         this.el.on('scroll', function() {
34761             Roo.log('fix random scolling');
34762             this.scrollTo('top',0); 
34763         });
34764     }
34765     content = content || this.content;
34766     if(content){
34767         this.setContent(content);
34768     }
34769     if(config && config.url){
34770         this.setUrl(this.url, this.params, this.loadOnce);
34771     }
34772     
34773     
34774     
34775     Roo.ContentPanel.superclass.constructor.call(this);
34776     
34777     if (this.view && typeof(this.view.xtype) != 'undefined') {
34778         this.view.el = this.el.appendChild(document.createElement("div"));
34779         this.view = Roo.factory(this.view); 
34780         this.view.render  &&  this.view.render(false, '');  
34781     }
34782     
34783     
34784     this.fireEvent('render', this);
34785 };
34786
34787 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34788     tabTip:'',
34789     setRegion : function(region){
34790         this.region = region;
34791         if(region){
34792            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34793         }else{
34794            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34795         } 
34796     },
34797     
34798     /**
34799      * Returns the toolbar for this Panel if one was configured. 
34800      * @return {Roo.Toolbar} 
34801      */
34802     getToolbar : function(){
34803         return this.toolbar;
34804     },
34805     
34806     setActiveState : function(active){
34807         this.active = active;
34808         if(!active){
34809             this.fireEvent("deactivate", this);
34810         }else{
34811             this.fireEvent("activate", this);
34812         }
34813     },
34814     /**
34815      * Updates this panel's element
34816      * @param {String} content The new content
34817      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34818     */
34819     setContent : function(content, loadScripts){
34820         this.el.update(content, loadScripts);
34821     },
34822
34823     ignoreResize : function(w, h){
34824         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34825             return true;
34826         }else{
34827             this.lastSize = {width: w, height: h};
34828             return false;
34829         }
34830     },
34831     /**
34832      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34833      * @return {Roo.UpdateManager} The UpdateManager
34834      */
34835     getUpdateManager : function(){
34836         return this.el.getUpdateManager();
34837     },
34838      /**
34839      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34840      * @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:
34841 <pre><code>
34842 panel.load({
34843     url: "your-url.php",
34844     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34845     callback: yourFunction,
34846     scope: yourObject, //(optional scope)
34847     discardUrl: false,
34848     nocache: false,
34849     text: "Loading...",
34850     timeout: 30,
34851     scripts: false
34852 });
34853 </code></pre>
34854      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34855      * 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.
34856      * @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}
34857      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34858      * @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.
34859      * @return {Roo.ContentPanel} this
34860      */
34861     load : function(){
34862         var um = this.el.getUpdateManager();
34863         um.update.apply(um, arguments);
34864         return this;
34865     },
34866
34867
34868     /**
34869      * 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.
34870      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34871      * @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)
34872      * @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)
34873      * @return {Roo.UpdateManager} The UpdateManager
34874      */
34875     setUrl : function(url, params, loadOnce){
34876         if(this.refreshDelegate){
34877             this.removeListener("activate", this.refreshDelegate);
34878         }
34879         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34880         this.on("activate", this.refreshDelegate);
34881         return this.el.getUpdateManager();
34882     },
34883     
34884     _handleRefresh : function(url, params, loadOnce){
34885         if(!loadOnce || !this.loaded){
34886             var updater = this.el.getUpdateManager();
34887             updater.update(url, params, this._setLoaded.createDelegate(this));
34888         }
34889     },
34890     
34891     _setLoaded : function(){
34892         this.loaded = true;
34893     }, 
34894     
34895     /**
34896      * Returns this panel's id
34897      * @return {String} 
34898      */
34899     getId : function(){
34900         return this.el.id;
34901     },
34902     
34903     /** 
34904      * Returns this panel's element - used by regiosn to add.
34905      * @return {Roo.Element} 
34906      */
34907     getEl : function(){
34908         return this.wrapEl || this.el;
34909     },
34910     
34911     adjustForComponents : function(width, height)
34912     {
34913         //Roo.log('adjustForComponents ');
34914         if(this.resizeEl != this.el){
34915             width -= this.el.getFrameWidth('lr');
34916             height -= this.el.getFrameWidth('tb');
34917         }
34918         if(this.toolbar){
34919             var te = this.toolbar.getEl();
34920             height -= te.getHeight();
34921             te.setWidth(width);
34922         }
34923         if(this.footer){
34924             var te = this.footer.getEl();
34925             Roo.log("footer:" + te.getHeight());
34926             
34927             height -= te.getHeight();
34928             te.setWidth(width);
34929         }
34930         
34931         
34932         if(this.adjustments){
34933             width += this.adjustments[0];
34934             height += this.adjustments[1];
34935         }
34936         return {"width": width, "height": height};
34937     },
34938     
34939     setSize : function(width, height){
34940         if(this.fitToFrame && !this.ignoreResize(width, height)){
34941             if(this.fitContainer && this.resizeEl != this.el){
34942                 this.el.setSize(width, height);
34943             }
34944             var size = this.adjustForComponents(width, height);
34945             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34946             this.fireEvent('resize', this, size.width, size.height);
34947         }
34948     },
34949     
34950     /**
34951      * Returns this panel's title
34952      * @return {String} 
34953      */
34954     getTitle : function(){
34955         return this.title;
34956     },
34957     
34958     /**
34959      * Set this panel's title
34960      * @param {String} title
34961      */
34962     setTitle : function(title){
34963         this.title = title;
34964         if(this.region){
34965             this.region.updatePanelTitle(this, title);
34966         }
34967     },
34968     
34969     /**
34970      * Returns true is this panel was configured to be closable
34971      * @return {Boolean} 
34972      */
34973     isClosable : function(){
34974         return this.closable;
34975     },
34976     
34977     beforeSlide : function(){
34978         this.el.clip();
34979         this.resizeEl.clip();
34980     },
34981     
34982     afterSlide : function(){
34983         this.el.unclip();
34984         this.resizeEl.unclip();
34985     },
34986     
34987     /**
34988      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34989      *   Will fail silently if the {@link #setUrl} method has not been called.
34990      *   This does not activate the panel, just updates its content.
34991      */
34992     refresh : function(){
34993         if(this.refreshDelegate){
34994            this.loaded = false;
34995            this.refreshDelegate();
34996         }
34997     },
34998     
34999     /**
35000      * Destroys this panel
35001      */
35002     destroy : function(){
35003         this.el.removeAllListeners();
35004         var tempEl = document.createElement("span");
35005         tempEl.appendChild(this.el.dom);
35006         tempEl.innerHTML = "";
35007         this.el.remove();
35008         this.el = null;
35009     },
35010     
35011     /**
35012      * form - if the content panel contains a form - this is a reference to it.
35013      * @type {Roo.form.Form}
35014      */
35015     form : false,
35016     /**
35017      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35018      *    This contains a reference to it.
35019      * @type {Roo.View}
35020      */
35021     view : false,
35022     
35023       /**
35024      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35025      * <pre><code>
35026
35027 layout.addxtype({
35028        xtype : 'Form',
35029        items: [ .... ]
35030    }
35031 );
35032
35033 </code></pre>
35034      * @param {Object} cfg Xtype definition of item to add.
35035      */
35036     
35037     addxtype : function(cfg) {
35038         // add form..
35039         if (cfg.xtype.match(/^Form$/)) {
35040             
35041             var el;
35042             //if (this.footer) {
35043             //    el = this.footer.container.insertSibling(false, 'before');
35044             //} else {
35045                 el = this.el.createChild();
35046             //}
35047
35048             this.form = new  Roo.form.Form(cfg);
35049             
35050             
35051             if ( this.form.allItems.length) this.form.render(el.dom);
35052             return this.form;
35053         }
35054         // should only have one of theses..
35055         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35056             // views.. should not be just added - used named prop 'view''
35057             
35058             cfg.el = this.el.appendChild(document.createElement("div"));
35059             // factory?
35060             
35061             var ret = new Roo.factory(cfg);
35062              
35063              ret.render && ret.render(false, ''); // render blank..
35064             this.view = ret;
35065             return ret;
35066         }
35067         return false;
35068     }
35069 });
35070
35071 /**
35072  * @class Roo.GridPanel
35073  * @extends Roo.ContentPanel
35074  * @constructor
35075  * Create a new GridPanel.
35076  * @param {Roo.grid.Grid} grid The grid for this panel
35077  * @param {String/Object} config A string to set only the panel's title, or a config object
35078  */
35079 Roo.GridPanel = function(grid, config){
35080     
35081   
35082     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35083         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35084         
35085     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35086     
35087     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35088     
35089     if(this.toolbar){
35090         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35091     }
35092     // xtype created footer. - not sure if will work as we normally have to render first..
35093     if (this.footer && !this.footer.el && this.footer.xtype) {
35094         
35095         this.footer.container = this.grid.getView().getFooterPanel(true);
35096         this.footer.dataSource = this.grid.dataSource;
35097         this.footer = Roo.factory(this.footer, Roo);
35098         
35099     }
35100     
35101     grid.monitorWindowResize = false; // turn off autosizing
35102     grid.autoHeight = false;
35103     grid.autoWidth = false;
35104     this.grid = grid;
35105     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35106 };
35107
35108 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35109     getId : function(){
35110         return this.grid.id;
35111     },
35112     
35113     /**
35114      * Returns the grid for this panel
35115      * @return {Roo.grid.Grid} 
35116      */
35117     getGrid : function(){
35118         return this.grid;    
35119     },
35120     
35121     setSize : function(width, height){
35122         if(!this.ignoreResize(width, height)){
35123             var grid = this.grid;
35124             var size = this.adjustForComponents(width, height);
35125             grid.getGridEl().setSize(size.width, size.height);
35126             grid.autoSize();
35127         }
35128     },
35129     
35130     beforeSlide : function(){
35131         this.grid.getView().scroller.clip();
35132     },
35133     
35134     afterSlide : function(){
35135         this.grid.getView().scroller.unclip();
35136     },
35137     
35138     destroy : function(){
35139         this.grid.destroy();
35140         delete this.grid;
35141         Roo.GridPanel.superclass.destroy.call(this); 
35142     }
35143 });
35144
35145
35146 /**
35147  * @class Roo.NestedLayoutPanel
35148  * @extends Roo.ContentPanel
35149  * @constructor
35150  * Create a new NestedLayoutPanel.
35151  * 
35152  * 
35153  * @param {Roo.BorderLayout} layout The layout for this panel
35154  * @param {String/Object} config A string to set only the title or a config object
35155  */
35156 Roo.NestedLayoutPanel = function(layout, config)
35157 {
35158     // construct with only one argument..
35159     /* FIXME - implement nicer consturctors
35160     if (layout.layout) {
35161         config = layout;
35162         layout = config.layout;
35163         delete config.layout;
35164     }
35165     if (layout.xtype && !layout.getEl) {
35166         // then layout needs constructing..
35167         layout = Roo.factory(layout, Roo);
35168     }
35169     */
35170     
35171     
35172     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35173     
35174     layout.monitorWindowResize = false; // turn off autosizing
35175     this.layout = layout;
35176     this.layout.getEl().addClass("x-layout-nested-layout");
35177     
35178     
35179     
35180     
35181 };
35182
35183 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35184
35185     setSize : function(width, height){
35186         if(!this.ignoreResize(width, height)){
35187             var size = this.adjustForComponents(width, height);
35188             var el = this.layout.getEl();
35189             el.setSize(size.width, size.height);
35190             var touch = el.dom.offsetWidth;
35191             this.layout.layout();
35192             // ie requires a double layout on the first pass
35193             if(Roo.isIE && !this.initialized){
35194                 this.initialized = true;
35195                 this.layout.layout();
35196             }
35197         }
35198     },
35199     
35200     // activate all subpanels if not currently active..
35201     
35202     setActiveState : function(active){
35203         this.active = active;
35204         if(!active){
35205             this.fireEvent("deactivate", this);
35206             return;
35207         }
35208         
35209         this.fireEvent("activate", this);
35210         // not sure if this should happen before or after..
35211         if (!this.layout) {
35212             return; // should not happen..
35213         }
35214         var reg = false;
35215         for (var r in this.layout.regions) {
35216             reg = this.layout.getRegion(r);
35217             if (reg.getActivePanel()) {
35218                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35219                 reg.setActivePanel(reg.getActivePanel());
35220                 continue;
35221             }
35222             if (!reg.panels.length) {
35223                 continue;
35224             }
35225             reg.showPanel(reg.getPanel(0));
35226         }
35227         
35228         
35229         
35230         
35231     },
35232     
35233     /**
35234      * Returns the nested BorderLayout for this panel
35235      * @return {Roo.BorderLayout} 
35236      */
35237     getLayout : function(){
35238         return this.layout;
35239     },
35240     
35241      /**
35242      * Adds a xtype elements to the layout of the nested panel
35243      * <pre><code>
35244
35245 panel.addxtype({
35246        xtype : 'ContentPanel',
35247        region: 'west',
35248        items: [ .... ]
35249    }
35250 );
35251
35252 panel.addxtype({
35253         xtype : 'NestedLayoutPanel',
35254         region: 'west',
35255         layout: {
35256            center: { },
35257            west: { }   
35258         },
35259         items : [ ... list of content panels or nested layout panels.. ]
35260    }
35261 );
35262 </code></pre>
35263      * @param {Object} cfg Xtype definition of item to add.
35264      */
35265     addxtype : function(cfg) {
35266         return this.layout.addxtype(cfg);
35267     
35268     }
35269 });
35270
35271 Roo.ScrollPanel = function(el, config, content){
35272     config = config || {};
35273     config.fitToFrame = true;
35274     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35275     
35276     this.el.dom.style.overflow = "hidden";
35277     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35278     this.el.removeClass("x-layout-inactive-content");
35279     this.el.on("mousewheel", this.onWheel, this);
35280
35281     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35282     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35283     up.unselectable(); down.unselectable();
35284     up.on("click", this.scrollUp, this);
35285     down.on("click", this.scrollDown, this);
35286     up.addClassOnOver("x-scroller-btn-over");
35287     down.addClassOnOver("x-scroller-btn-over");
35288     up.addClassOnClick("x-scroller-btn-click");
35289     down.addClassOnClick("x-scroller-btn-click");
35290     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35291
35292     this.resizeEl = this.el;
35293     this.el = wrap; this.up = up; this.down = down;
35294 };
35295
35296 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35297     increment : 100,
35298     wheelIncrement : 5,
35299     scrollUp : function(){
35300         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35301     },
35302
35303     scrollDown : function(){
35304         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35305     },
35306
35307     afterScroll : function(){
35308         var el = this.resizeEl;
35309         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35310         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35311         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35312     },
35313
35314     setSize : function(){
35315         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35316         this.afterScroll();
35317     },
35318
35319     onWheel : function(e){
35320         var d = e.getWheelDelta();
35321         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35322         this.afterScroll();
35323         e.stopEvent();
35324     },
35325
35326     setContent : function(content, loadScripts){
35327         this.resizeEl.update(content, loadScripts);
35328     }
35329
35330 });
35331
35332
35333
35334
35335
35336
35337
35338
35339
35340 /**
35341  * @class Roo.TreePanel
35342  * @extends Roo.ContentPanel
35343  * @constructor
35344  * Create a new TreePanel. - defaults to fit/scoll contents.
35345  * @param {String/Object} config A string to set only the panel's title, or a config object
35346  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35347  */
35348 Roo.TreePanel = function(config){
35349     var el = config.el;
35350     var tree = config.tree;
35351     delete config.tree; 
35352     delete config.el; // hopefull!
35353     
35354     // wrapper for IE7 strict & safari scroll issue
35355     
35356     var treeEl = el.createChild();
35357     config.resizeEl = treeEl;
35358     
35359     
35360     
35361     Roo.TreePanel.superclass.constructor.call(this, el, config);
35362  
35363  
35364     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35365     //console.log(tree);
35366     this.on('activate', function()
35367     {
35368         if (this.tree.rendered) {
35369             return;
35370         }
35371         //console.log('render tree');
35372         this.tree.render();
35373     });
35374     // this should not be needed.. - it's actually the 'el' that resizes?
35375     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35376     
35377     //this.on('resize',  function (cp, w, h) {
35378     //        this.tree.innerCt.setWidth(w);
35379     //        this.tree.innerCt.setHeight(h);
35380     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35381     //});
35382
35383         
35384     
35385 };
35386
35387 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35388     fitToFrame : true,
35389     autoScroll : true
35390 });
35391
35392
35393
35394
35395
35396
35397
35398
35399
35400
35401
35402 /*
35403  * Based on:
35404  * Ext JS Library 1.1.1
35405  * Copyright(c) 2006-2007, Ext JS, LLC.
35406  *
35407  * Originally Released Under LGPL - original licence link has changed is not relivant.
35408  *
35409  * Fork - LGPL
35410  * <script type="text/javascript">
35411  */
35412  
35413
35414 /**
35415  * @class Roo.ReaderLayout
35416  * @extends Roo.BorderLayout
35417  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35418  * center region containing two nested regions (a top one for a list view and one for item preview below),
35419  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35420  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35421  * expedites the setup of the overall layout and regions for this common application style.
35422  * Example:
35423  <pre><code>
35424 var reader = new Roo.ReaderLayout();
35425 var CP = Roo.ContentPanel;  // shortcut for adding
35426
35427 reader.beginUpdate();
35428 reader.add("north", new CP("north", "North"));
35429 reader.add("west", new CP("west", {title: "West"}));
35430 reader.add("east", new CP("east", {title: "East"}));
35431
35432 reader.regions.listView.add(new CP("listView", "List"));
35433 reader.regions.preview.add(new CP("preview", "Preview"));
35434 reader.endUpdate();
35435 </code></pre>
35436 * @constructor
35437 * Create a new ReaderLayout
35438 * @param {Object} config Configuration options
35439 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35440 * document.body if omitted)
35441 */
35442 Roo.ReaderLayout = function(config, renderTo){
35443     var c = config || {size:{}};
35444     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35445         north: c.north !== false ? Roo.apply({
35446             split:false,
35447             initialSize: 32,
35448             titlebar: false
35449         }, c.north) : false,
35450         west: c.west !== false ? Roo.apply({
35451             split:true,
35452             initialSize: 200,
35453             minSize: 175,
35454             maxSize: 400,
35455             titlebar: true,
35456             collapsible: true,
35457             animate: true,
35458             margins:{left:5,right:0,bottom:5,top:5},
35459             cmargins:{left:5,right:5,bottom:5,top:5}
35460         }, c.west) : false,
35461         east: c.east !== false ? Roo.apply({
35462             split:true,
35463             initialSize: 200,
35464             minSize: 175,
35465             maxSize: 400,
35466             titlebar: true,
35467             collapsible: true,
35468             animate: true,
35469             margins:{left:0,right:5,bottom:5,top:5},
35470             cmargins:{left:5,right:5,bottom:5,top:5}
35471         }, c.east) : false,
35472         center: Roo.apply({
35473             tabPosition: 'top',
35474             autoScroll:false,
35475             closeOnTab: true,
35476             titlebar:false,
35477             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35478         }, c.center)
35479     });
35480
35481     this.el.addClass('x-reader');
35482
35483     this.beginUpdate();
35484
35485     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35486         south: c.preview !== false ? Roo.apply({
35487             split:true,
35488             initialSize: 200,
35489             minSize: 100,
35490             autoScroll:true,
35491             collapsible:true,
35492             titlebar: true,
35493             cmargins:{top:5,left:0, right:0, bottom:0}
35494         }, c.preview) : false,
35495         center: Roo.apply({
35496             autoScroll:false,
35497             titlebar:false,
35498             minHeight:200
35499         }, c.listView)
35500     });
35501     this.add('center', new Roo.NestedLayoutPanel(inner,
35502             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35503
35504     this.endUpdate();
35505
35506     this.regions.preview = inner.getRegion('south');
35507     this.regions.listView = inner.getRegion('center');
35508 };
35509
35510 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35511  * Based on:
35512  * Ext JS Library 1.1.1
35513  * Copyright(c) 2006-2007, Ext JS, LLC.
35514  *
35515  * Originally Released Under LGPL - original licence link has changed is not relivant.
35516  *
35517  * Fork - LGPL
35518  * <script type="text/javascript">
35519  */
35520  
35521 /**
35522  * @class Roo.grid.Grid
35523  * @extends Roo.util.Observable
35524  * This class represents the primary interface of a component based grid control.
35525  * <br><br>Usage:<pre><code>
35526  var grid = new Roo.grid.Grid("my-container-id", {
35527      ds: myDataStore,
35528      cm: myColModel,
35529      selModel: mySelectionModel,
35530      autoSizeColumns: true,
35531      monitorWindowResize: false,
35532      trackMouseOver: true
35533  });
35534  // set any options
35535  grid.render();
35536  * </code></pre>
35537  * <b>Common Problems:</b><br/>
35538  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35539  * element will correct this<br/>
35540  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35541  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35542  * are unpredictable.<br/>
35543  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35544  * grid to calculate dimensions/offsets.<br/>
35545   * @constructor
35546  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35547  * The container MUST have some type of size defined for the grid to fill. The container will be
35548  * automatically set to position relative if it isn't already.
35549  * @param {Object} config A config object that sets properties on this grid.
35550  */
35551 Roo.grid.Grid = function(container, config){
35552         // initialize the container
35553         this.container = Roo.get(container);
35554         this.container.update("");
35555         this.container.setStyle("overflow", "hidden");
35556     this.container.addClass('x-grid-container');
35557
35558     this.id = this.container.id;
35559
35560     Roo.apply(this, config);
35561     // check and correct shorthanded configs
35562     if(this.ds){
35563         this.dataSource = this.ds;
35564         delete this.ds;
35565     }
35566     if(this.cm){
35567         this.colModel = this.cm;
35568         delete this.cm;
35569     }
35570     if(this.sm){
35571         this.selModel = this.sm;
35572         delete this.sm;
35573     }
35574
35575     if (this.selModel) {
35576         this.selModel = Roo.factory(this.selModel, Roo.grid);
35577         this.sm = this.selModel;
35578         this.sm.xmodule = this.xmodule || false;
35579     }
35580     if (typeof(this.colModel.config) == 'undefined') {
35581         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35582         this.cm = this.colModel;
35583         this.cm.xmodule = this.xmodule || false;
35584     }
35585     if (this.dataSource) {
35586         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35587         this.ds = this.dataSource;
35588         this.ds.xmodule = this.xmodule || false;
35589          
35590     }
35591     
35592     
35593     
35594     if(this.width){
35595         this.container.setWidth(this.width);
35596     }
35597
35598     if(this.height){
35599         this.container.setHeight(this.height);
35600     }
35601     /** @private */
35602         this.addEvents({
35603         // raw events
35604         /**
35605          * @event click
35606          * The raw click event for the entire grid.
35607          * @param {Roo.EventObject} e
35608          */
35609         "click" : true,
35610         /**
35611          * @event dblclick
35612          * The raw dblclick event for the entire grid.
35613          * @param {Roo.EventObject} e
35614          */
35615         "dblclick" : true,
35616         /**
35617          * @event contextmenu
35618          * The raw contextmenu event for the entire grid.
35619          * @param {Roo.EventObject} e
35620          */
35621         "contextmenu" : true,
35622         /**
35623          * @event mousedown
35624          * The raw mousedown event for the entire grid.
35625          * @param {Roo.EventObject} e
35626          */
35627         "mousedown" : true,
35628         /**
35629          * @event mouseup
35630          * The raw mouseup event for the entire grid.
35631          * @param {Roo.EventObject} e
35632          */
35633         "mouseup" : true,
35634         /**
35635          * @event mouseover
35636          * The raw mouseover event for the entire grid.
35637          * @param {Roo.EventObject} e
35638          */
35639         "mouseover" : true,
35640         /**
35641          * @event mouseout
35642          * The raw mouseout event for the entire grid.
35643          * @param {Roo.EventObject} e
35644          */
35645         "mouseout" : true,
35646         /**
35647          * @event keypress
35648          * The raw keypress event for the entire grid.
35649          * @param {Roo.EventObject} e
35650          */
35651         "keypress" : true,
35652         /**
35653          * @event keydown
35654          * The raw keydown event for the entire grid.
35655          * @param {Roo.EventObject} e
35656          */
35657         "keydown" : true,
35658
35659         // custom events
35660
35661         /**
35662          * @event cellclick
35663          * Fires when a cell is clicked
35664          * @param {Grid} this
35665          * @param {Number} rowIndex
35666          * @param {Number} columnIndex
35667          * @param {Roo.EventObject} e
35668          */
35669         "cellclick" : true,
35670         /**
35671          * @event celldblclick
35672          * Fires when a cell is double clicked
35673          * @param {Grid} this
35674          * @param {Number} rowIndex
35675          * @param {Number} columnIndex
35676          * @param {Roo.EventObject} e
35677          */
35678         "celldblclick" : true,
35679         /**
35680          * @event rowclick
35681          * Fires when a row is clicked
35682          * @param {Grid} this
35683          * @param {Number} rowIndex
35684          * @param {Roo.EventObject} e
35685          */
35686         "rowclick" : true,
35687         /**
35688          * @event rowdblclick
35689          * Fires when a row is double clicked
35690          * @param {Grid} this
35691          * @param {Number} rowIndex
35692          * @param {Roo.EventObject} e
35693          */
35694         "rowdblclick" : true,
35695         /**
35696          * @event headerclick
35697          * Fires when a header is clicked
35698          * @param {Grid} this
35699          * @param {Number} columnIndex
35700          * @param {Roo.EventObject} e
35701          */
35702         "headerclick" : true,
35703         /**
35704          * @event headerdblclick
35705          * Fires when a header cell is double clicked
35706          * @param {Grid} this
35707          * @param {Number} columnIndex
35708          * @param {Roo.EventObject} e
35709          */
35710         "headerdblclick" : true,
35711         /**
35712          * @event rowcontextmenu
35713          * Fires when a row is right clicked
35714          * @param {Grid} this
35715          * @param {Number} rowIndex
35716          * @param {Roo.EventObject} e
35717          */
35718         "rowcontextmenu" : true,
35719         /**
35720          * @event cellcontextmenu
35721          * Fires when a cell is right clicked
35722          * @param {Grid} this
35723          * @param {Number} rowIndex
35724          * @param {Number} cellIndex
35725          * @param {Roo.EventObject} e
35726          */
35727          "cellcontextmenu" : true,
35728         /**
35729          * @event headercontextmenu
35730          * Fires when a header is right clicked
35731          * @param {Grid} this
35732          * @param {Number} columnIndex
35733          * @param {Roo.EventObject} e
35734          */
35735         "headercontextmenu" : true,
35736         /**
35737          * @event bodyscroll
35738          * Fires when the body element is scrolled
35739          * @param {Number} scrollLeft
35740          * @param {Number} scrollTop
35741          */
35742         "bodyscroll" : true,
35743         /**
35744          * @event columnresize
35745          * Fires when the user resizes a column
35746          * @param {Number} columnIndex
35747          * @param {Number} newSize
35748          */
35749         "columnresize" : true,
35750         /**
35751          * @event columnmove
35752          * Fires when the user moves a column
35753          * @param {Number} oldIndex
35754          * @param {Number} newIndex
35755          */
35756         "columnmove" : true,
35757         /**
35758          * @event startdrag
35759          * Fires when row(s) start being dragged
35760          * @param {Grid} this
35761          * @param {Roo.GridDD} dd The drag drop object
35762          * @param {event} e The raw browser event
35763          */
35764         "startdrag" : true,
35765         /**
35766          * @event enddrag
35767          * Fires when a drag operation is complete
35768          * @param {Grid} this
35769          * @param {Roo.GridDD} dd The drag drop object
35770          * @param {event} e The raw browser event
35771          */
35772         "enddrag" : true,
35773         /**
35774          * @event dragdrop
35775          * Fires when dragged row(s) are dropped on a valid DD target
35776          * @param {Grid} this
35777          * @param {Roo.GridDD} dd The drag drop object
35778          * @param {String} targetId The target drag drop object
35779          * @param {event} e The raw browser event
35780          */
35781         "dragdrop" : true,
35782         /**
35783          * @event dragover
35784          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35785          * @param {Grid} this
35786          * @param {Roo.GridDD} dd The drag drop object
35787          * @param {String} targetId The target drag drop object
35788          * @param {event} e The raw browser event
35789          */
35790         "dragover" : true,
35791         /**
35792          * @event dragenter
35793          *  Fires when the dragged row(s) first cross another DD target while being dragged
35794          * @param {Grid} this
35795          * @param {Roo.GridDD} dd The drag drop object
35796          * @param {String} targetId The target drag drop object
35797          * @param {event} e The raw browser event
35798          */
35799         "dragenter" : true,
35800         /**
35801          * @event dragout
35802          * Fires when the dragged row(s) leave another DD target while being dragged
35803          * @param {Grid} this
35804          * @param {Roo.GridDD} dd The drag drop object
35805          * @param {String} targetId The target drag drop object
35806          * @param {event} e The raw browser event
35807          */
35808         "dragout" : true,
35809         /**
35810          * @event rowclass
35811          * Fires when a row is rendered, so you can change add a style to it.
35812          * @param {GridView} gridview   The grid view
35813          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35814          */
35815         'rowclass' : true,
35816
35817         /**
35818          * @event render
35819          * Fires when the grid is rendered
35820          * @param {Grid} grid
35821          */
35822         'render' : true
35823     });
35824
35825     Roo.grid.Grid.superclass.constructor.call(this);
35826 };
35827 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35828     
35829     /**
35830      * @cfg {String} ddGroup - drag drop group.
35831      */
35832
35833     /**
35834      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35835      */
35836     minColumnWidth : 25,
35837
35838     /**
35839      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35840      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35841      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35842      */
35843     autoSizeColumns : false,
35844
35845     /**
35846      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35847      */
35848     autoSizeHeaders : true,
35849
35850     /**
35851      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35852      */
35853     monitorWindowResize : true,
35854
35855     /**
35856      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35857      * rows measured to get a columns size. Default is 0 (all rows).
35858      */
35859     maxRowsToMeasure : 0,
35860
35861     /**
35862      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35863      */
35864     trackMouseOver : true,
35865
35866     /**
35867     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35868     */
35869     
35870     /**
35871     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35872     */
35873     enableDragDrop : false,
35874     
35875     /**
35876     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35877     */
35878     enableColumnMove : true,
35879     
35880     /**
35881     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35882     */
35883     enableColumnHide : true,
35884     
35885     /**
35886     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35887     */
35888     enableRowHeightSync : false,
35889     
35890     /**
35891     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35892     */
35893     stripeRows : true,
35894     
35895     /**
35896     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35897     */
35898     autoHeight : false,
35899
35900     /**
35901      * @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.
35902      */
35903     autoExpandColumn : false,
35904
35905     /**
35906     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35907     * Default is 50.
35908     */
35909     autoExpandMin : 50,
35910
35911     /**
35912     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35913     */
35914     autoExpandMax : 1000,
35915
35916     /**
35917     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35918     */
35919     view : null,
35920
35921     /**
35922     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35923     */
35924     loadMask : false,
35925     /**
35926     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35927     */
35928     dropTarget: false,
35929     
35930    
35931     
35932     // private
35933     rendered : false,
35934
35935     /**
35936     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35937     * of a fixed width. Default is false.
35938     */
35939     /**
35940     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35941     */
35942     /**
35943      * Called once after all setup has been completed and the grid is ready to be rendered.
35944      * @return {Roo.grid.Grid} this
35945      */
35946     render : function()
35947     {
35948         var c = this.container;
35949         // try to detect autoHeight/width mode
35950         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35951             this.autoHeight = true;
35952         }
35953         var view = this.getView();
35954         view.init(this);
35955
35956         c.on("click", this.onClick, this);
35957         c.on("dblclick", this.onDblClick, this);
35958         c.on("contextmenu", this.onContextMenu, this);
35959         c.on("keydown", this.onKeyDown, this);
35960         if (Roo.isTouch) {
35961             c.on("touchstart", this.onTouchStart, this);
35962         }
35963
35964         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35965
35966         this.getSelectionModel().init(this);
35967
35968         view.render();
35969
35970         if(this.loadMask){
35971             this.loadMask = new Roo.LoadMask(this.container,
35972                     Roo.apply({store:this.dataSource}, this.loadMask));
35973         }
35974         
35975         
35976         if (this.toolbar && this.toolbar.xtype) {
35977             this.toolbar.container = this.getView().getHeaderPanel(true);
35978             this.toolbar = new Roo.Toolbar(this.toolbar);
35979         }
35980         if (this.footer && this.footer.xtype) {
35981             this.footer.dataSource = this.getDataSource();
35982             this.footer.container = this.getView().getFooterPanel(true);
35983             this.footer = Roo.factory(this.footer, Roo);
35984         }
35985         if (this.dropTarget && this.dropTarget.xtype) {
35986             delete this.dropTarget.xtype;
35987             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35988         }
35989         
35990         
35991         this.rendered = true;
35992         this.fireEvent('render', this);
35993         return this;
35994     },
35995
35996         /**
35997          * Reconfigures the grid to use a different Store and Column Model.
35998          * The View will be bound to the new objects and refreshed.
35999          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36000          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36001          */
36002     reconfigure : function(dataSource, colModel){
36003         if(this.loadMask){
36004             this.loadMask.destroy();
36005             this.loadMask = new Roo.LoadMask(this.container,
36006                     Roo.apply({store:dataSource}, this.loadMask));
36007         }
36008         this.view.bind(dataSource, colModel);
36009         this.dataSource = dataSource;
36010         this.colModel = colModel;
36011         this.view.refresh(true);
36012     },
36013
36014     // private
36015     onKeyDown : function(e){
36016         this.fireEvent("keydown", e);
36017     },
36018
36019     /**
36020      * Destroy this grid.
36021      * @param {Boolean} removeEl True to remove the element
36022      */
36023     destroy : function(removeEl, keepListeners){
36024         if(this.loadMask){
36025             this.loadMask.destroy();
36026         }
36027         var c = this.container;
36028         c.removeAllListeners();
36029         this.view.destroy();
36030         this.colModel.purgeListeners();
36031         if(!keepListeners){
36032             this.purgeListeners();
36033         }
36034         c.update("");
36035         if(removeEl === true){
36036             c.remove();
36037         }
36038     },
36039
36040     // private
36041     processEvent : function(name, e){
36042         // does this fire select???
36043         Roo.log('grid:processEvent '  + name);
36044         
36045         if (name != 'touchstart' ) {
36046             this.fireEvent(name, e);    
36047         }
36048         
36049         var t = e.getTarget();
36050         var v = this.view;
36051         var header = v.findHeaderIndex(t);
36052         if(header !== false){
36053             var ename = name == 'touchstart' ? 'click' : name;
36054              
36055             this.fireEvent("header" + ename, this, header, e);
36056         }else{
36057             var row = v.findRowIndex(t);
36058             var cell = v.findCellIndex(t);
36059             if (name == 'touchstart') {
36060                 // first touch is always a click.
36061                 // hopefull this happens after selection is updated.?
36062                 name = false;
36063                 
36064                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36065                     var cs = this.selModel.getSelectedCell();
36066                     if (row == cs[0] && cell == cs[1]){
36067                         name = 'dblclick';
36068                     }
36069                 }
36070                 if (typeof(this.selModel.getSelections) != 'undefined') {
36071                     var cs = this.selModel.getSelections();
36072                     var ds = this.dataSource;
36073                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36074                         name = 'dblclick';
36075                     }
36076                 }
36077                 if (!name) {
36078                     return;
36079                 }
36080             }
36081             
36082             
36083             if(row !== false){
36084                 this.fireEvent("row" + name, this, row, e);
36085                 if(cell !== false){
36086                     this.fireEvent("cell" + name, this, row, cell, e);
36087                 }
36088             }
36089         }
36090     },
36091
36092     // private
36093     onClick : function(e){
36094         this.processEvent("click", e);
36095     },
36096    // private
36097     onTouchStart : function(e){
36098         this.processEvent("touchstart", e);
36099     },
36100
36101     // private
36102     onContextMenu : function(e, t){
36103         this.processEvent("contextmenu", e);
36104     },
36105
36106     // private
36107     onDblClick : function(e){
36108         this.processEvent("dblclick", e);
36109     },
36110
36111     // private
36112     walkCells : function(row, col, step, fn, scope){
36113         var cm = this.colModel, clen = cm.getColumnCount();
36114         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36115         if(step < 0){
36116             if(col < 0){
36117                 row--;
36118                 first = false;
36119             }
36120             while(row >= 0){
36121                 if(!first){
36122                     col = clen-1;
36123                 }
36124                 first = false;
36125                 while(col >= 0){
36126                     if(fn.call(scope || this, row, col, cm) === true){
36127                         return [row, col];
36128                     }
36129                     col--;
36130                 }
36131                 row--;
36132             }
36133         } else {
36134             if(col >= clen){
36135                 row++;
36136                 first = false;
36137             }
36138             while(row < rlen){
36139                 if(!first){
36140                     col = 0;
36141                 }
36142                 first = false;
36143                 while(col < clen){
36144                     if(fn.call(scope || this, row, col, cm) === true){
36145                         return [row, col];
36146                     }
36147                     col++;
36148                 }
36149                 row++;
36150             }
36151         }
36152         return null;
36153     },
36154
36155     // private
36156     getSelections : function(){
36157         return this.selModel.getSelections();
36158     },
36159
36160     /**
36161      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36162      * but if manual update is required this method will initiate it.
36163      */
36164     autoSize : function(){
36165         if(this.rendered){
36166             this.view.layout();
36167             if(this.view.adjustForScroll){
36168                 this.view.adjustForScroll();
36169             }
36170         }
36171     },
36172
36173     /**
36174      * Returns the grid's underlying element.
36175      * @return {Element} The element
36176      */
36177     getGridEl : function(){
36178         return this.container;
36179     },
36180
36181     // private for compatibility, overridden by editor grid
36182     stopEditing : function(){},
36183
36184     /**
36185      * Returns the grid's SelectionModel.
36186      * @return {SelectionModel}
36187      */
36188     getSelectionModel : function(){
36189         if(!this.selModel){
36190             this.selModel = new Roo.grid.RowSelectionModel();
36191         }
36192         return this.selModel;
36193     },
36194
36195     /**
36196      * Returns the grid's DataSource.
36197      * @return {DataSource}
36198      */
36199     getDataSource : function(){
36200         return this.dataSource;
36201     },
36202
36203     /**
36204      * Returns the grid's ColumnModel.
36205      * @return {ColumnModel}
36206      */
36207     getColumnModel : function(){
36208         return this.colModel;
36209     },
36210
36211     /**
36212      * Returns the grid's GridView object.
36213      * @return {GridView}
36214      */
36215     getView : function(){
36216         if(!this.view){
36217             this.view = new Roo.grid.GridView(this.viewConfig);
36218         }
36219         return this.view;
36220     },
36221     /**
36222      * Called to get grid's drag proxy text, by default returns this.ddText.
36223      * @return {String}
36224      */
36225     getDragDropText : function(){
36226         var count = this.selModel.getCount();
36227         return String.format(this.ddText, count, count == 1 ? '' : 's');
36228     }
36229 });
36230 /**
36231  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36232  * %0 is replaced with the number of selected rows.
36233  * @type String
36234  */
36235 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36236  * Based on:
36237  * Ext JS Library 1.1.1
36238  * Copyright(c) 2006-2007, Ext JS, LLC.
36239  *
36240  * Originally Released Under LGPL - original licence link has changed is not relivant.
36241  *
36242  * Fork - LGPL
36243  * <script type="text/javascript">
36244  */
36245  
36246 Roo.grid.AbstractGridView = function(){
36247         this.grid = null;
36248         
36249         this.events = {
36250             "beforerowremoved" : true,
36251             "beforerowsinserted" : true,
36252             "beforerefresh" : true,
36253             "rowremoved" : true,
36254             "rowsinserted" : true,
36255             "rowupdated" : true,
36256             "refresh" : true
36257         };
36258     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36259 };
36260
36261 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36262     rowClass : "x-grid-row",
36263     cellClass : "x-grid-cell",
36264     tdClass : "x-grid-td",
36265     hdClass : "x-grid-hd",
36266     splitClass : "x-grid-hd-split",
36267     
36268     init: function(grid){
36269         this.grid = grid;
36270                 var cid = this.grid.getGridEl().id;
36271         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36272         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36273         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36274         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36275         },
36276         
36277     getColumnRenderers : function(){
36278         var renderers = [];
36279         var cm = this.grid.colModel;
36280         var colCount = cm.getColumnCount();
36281         for(var i = 0; i < colCount; i++){
36282             renderers[i] = cm.getRenderer(i);
36283         }
36284         return renderers;
36285     },
36286     
36287     getColumnIds : function(){
36288         var ids = [];
36289         var cm = this.grid.colModel;
36290         var colCount = cm.getColumnCount();
36291         for(var i = 0; i < colCount; i++){
36292             ids[i] = cm.getColumnId(i);
36293         }
36294         return ids;
36295     },
36296     
36297     getDataIndexes : function(){
36298         if(!this.indexMap){
36299             this.indexMap = this.buildIndexMap();
36300         }
36301         return this.indexMap.colToData;
36302     },
36303     
36304     getColumnIndexByDataIndex : function(dataIndex){
36305         if(!this.indexMap){
36306             this.indexMap = this.buildIndexMap();
36307         }
36308         return this.indexMap.dataToCol[dataIndex];
36309     },
36310     
36311     /**
36312      * Set a css style for a column dynamically. 
36313      * @param {Number} colIndex The index of the column
36314      * @param {String} name The css property name
36315      * @param {String} value The css value
36316      */
36317     setCSSStyle : function(colIndex, name, value){
36318         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36319         Roo.util.CSS.updateRule(selector, name, value);
36320     },
36321     
36322     generateRules : function(cm){
36323         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36324         Roo.util.CSS.removeStyleSheet(rulesId);
36325         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36326             var cid = cm.getColumnId(i);
36327             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36328                          this.tdSelector, cid, " {\n}\n",
36329                          this.hdSelector, cid, " {\n}\n",
36330                          this.splitSelector, cid, " {\n}\n");
36331         }
36332         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36333     }
36334 });/*
36335  * Based on:
36336  * Ext JS Library 1.1.1
36337  * Copyright(c) 2006-2007, Ext JS, LLC.
36338  *
36339  * Originally Released Under LGPL - original licence link has changed is not relivant.
36340  *
36341  * Fork - LGPL
36342  * <script type="text/javascript">
36343  */
36344
36345 // private
36346 // This is a support class used internally by the Grid components
36347 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36348     this.grid = grid;
36349     this.view = grid.getView();
36350     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36351     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36352     if(hd2){
36353         this.setHandleElId(Roo.id(hd));
36354         this.setOuterHandleElId(Roo.id(hd2));
36355     }
36356     this.scroll = false;
36357 };
36358 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36359     maxDragWidth: 120,
36360     getDragData : function(e){
36361         var t = Roo.lib.Event.getTarget(e);
36362         var h = this.view.findHeaderCell(t);
36363         if(h){
36364             return {ddel: h.firstChild, header:h};
36365         }
36366         return false;
36367     },
36368
36369     onInitDrag : function(e){
36370         this.view.headersDisabled = true;
36371         var clone = this.dragData.ddel.cloneNode(true);
36372         clone.id = Roo.id();
36373         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36374         this.proxy.update(clone);
36375         return true;
36376     },
36377
36378     afterValidDrop : function(){
36379         var v = this.view;
36380         setTimeout(function(){
36381             v.headersDisabled = false;
36382         }, 50);
36383     },
36384
36385     afterInvalidDrop : function(){
36386         var v = this.view;
36387         setTimeout(function(){
36388             v.headersDisabled = false;
36389         }, 50);
36390     }
36391 });
36392 /*
36393  * Based on:
36394  * Ext JS Library 1.1.1
36395  * Copyright(c) 2006-2007, Ext JS, LLC.
36396  *
36397  * Originally Released Under LGPL - original licence link has changed is not relivant.
36398  *
36399  * Fork - LGPL
36400  * <script type="text/javascript">
36401  */
36402 // private
36403 // This is a support class used internally by the Grid components
36404 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36405     this.grid = grid;
36406     this.view = grid.getView();
36407     // split the proxies so they don't interfere with mouse events
36408     this.proxyTop = Roo.DomHelper.append(document.body, {
36409         cls:"col-move-top", html:"&#160;"
36410     }, true);
36411     this.proxyBottom = Roo.DomHelper.append(document.body, {
36412         cls:"col-move-bottom", html:"&#160;"
36413     }, true);
36414     this.proxyTop.hide = this.proxyBottom.hide = function(){
36415         this.setLeftTop(-100,-100);
36416         this.setStyle("visibility", "hidden");
36417     };
36418     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36419     // temporarily disabled
36420     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36421     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36422 };
36423 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36424     proxyOffsets : [-4, -9],
36425     fly: Roo.Element.fly,
36426
36427     getTargetFromEvent : function(e){
36428         var t = Roo.lib.Event.getTarget(e);
36429         var cindex = this.view.findCellIndex(t);
36430         if(cindex !== false){
36431             return this.view.getHeaderCell(cindex);
36432         }
36433         return null;
36434     },
36435
36436     nextVisible : function(h){
36437         var v = this.view, cm = this.grid.colModel;
36438         h = h.nextSibling;
36439         while(h){
36440             if(!cm.isHidden(v.getCellIndex(h))){
36441                 return h;
36442             }
36443             h = h.nextSibling;
36444         }
36445         return null;
36446     },
36447
36448     prevVisible : function(h){
36449         var v = this.view, cm = this.grid.colModel;
36450         h = h.prevSibling;
36451         while(h){
36452             if(!cm.isHidden(v.getCellIndex(h))){
36453                 return h;
36454             }
36455             h = h.prevSibling;
36456         }
36457         return null;
36458     },
36459
36460     positionIndicator : function(h, n, e){
36461         var x = Roo.lib.Event.getPageX(e);
36462         var r = Roo.lib.Dom.getRegion(n.firstChild);
36463         var px, pt, py = r.top + this.proxyOffsets[1];
36464         if((r.right - x) <= (r.right-r.left)/2){
36465             px = r.right+this.view.borderWidth;
36466             pt = "after";
36467         }else{
36468             px = r.left;
36469             pt = "before";
36470         }
36471         var oldIndex = this.view.getCellIndex(h);
36472         var newIndex = this.view.getCellIndex(n);
36473
36474         if(this.grid.colModel.isFixed(newIndex)){
36475             return false;
36476         }
36477
36478         var locked = this.grid.colModel.isLocked(newIndex);
36479
36480         if(pt == "after"){
36481             newIndex++;
36482         }
36483         if(oldIndex < newIndex){
36484             newIndex--;
36485         }
36486         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36487             return false;
36488         }
36489         px +=  this.proxyOffsets[0];
36490         this.proxyTop.setLeftTop(px, py);
36491         this.proxyTop.show();
36492         if(!this.bottomOffset){
36493             this.bottomOffset = this.view.mainHd.getHeight();
36494         }
36495         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36496         this.proxyBottom.show();
36497         return pt;
36498     },
36499
36500     onNodeEnter : function(n, dd, e, data){
36501         if(data.header != n){
36502             this.positionIndicator(data.header, n, e);
36503         }
36504     },
36505
36506     onNodeOver : function(n, dd, e, data){
36507         var result = false;
36508         if(data.header != n){
36509             result = this.positionIndicator(data.header, n, e);
36510         }
36511         if(!result){
36512             this.proxyTop.hide();
36513             this.proxyBottom.hide();
36514         }
36515         return result ? this.dropAllowed : this.dropNotAllowed;
36516     },
36517
36518     onNodeOut : function(n, dd, e, data){
36519         this.proxyTop.hide();
36520         this.proxyBottom.hide();
36521     },
36522
36523     onNodeDrop : function(n, dd, e, data){
36524         var h = data.header;
36525         if(h != n){
36526             var cm = this.grid.colModel;
36527             var x = Roo.lib.Event.getPageX(e);
36528             var r = Roo.lib.Dom.getRegion(n.firstChild);
36529             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36530             var oldIndex = this.view.getCellIndex(h);
36531             var newIndex = this.view.getCellIndex(n);
36532             var locked = cm.isLocked(newIndex);
36533             if(pt == "after"){
36534                 newIndex++;
36535             }
36536             if(oldIndex < newIndex){
36537                 newIndex--;
36538             }
36539             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36540                 return false;
36541             }
36542             cm.setLocked(oldIndex, locked, true);
36543             cm.moveColumn(oldIndex, newIndex);
36544             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36545             return true;
36546         }
36547         return false;
36548     }
36549 });
36550 /*
36551  * Based on:
36552  * Ext JS Library 1.1.1
36553  * Copyright(c) 2006-2007, Ext JS, LLC.
36554  *
36555  * Originally Released Under LGPL - original licence link has changed is not relivant.
36556  *
36557  * Fork - LGPL
36558  * <script type="text/javascript">
36559  */
36560   
36561 /**
36562  * @class Roo.grid.GridView
36563  * @extends Roo.util.Observable
36564  *
36565  * @constructor
36566  * @param {Object} config
36567  */
36568 Roo.grid.GridView = function(config){
36569     Roo.grid.GridView.superclass.constructor.call(this);
36570     this.el = null;
36571
36572     Roo.apply(this, config);
36573 };
36574
36575 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36576
36577     unselectable :  'unselectable="on"',
36578     unselectableCls :  'x-unselectable',
36579     
36580     
36581     rowClass : "x-grid-row",
36582
36583     cellClass : "x-grid-col",
36584
36585     tdClass : "x-grid-td",
36586
36587     hdClass : "x-grid-hd",
36588
36589     splitClass : "x-grid-split",
36590
36591     sortClasses : ["sort-asc", "sort-desc"],
36592
36593     enableMoveAnim : false,
36594
36595     hlColor: "C3DAF9",
36596
36597     dh : Roo.DomHelper,
36598
36599     fly : Roo.Element.fly,
36600
36601     css : Roo.util.CSS,
36602
36603     borderWidth: 1,
36604
36605     splitOffset: 3,
36606
36607     scrollIncrement : 22,
36608
36609     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36610
36611     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36612
36613     bind : function(ds, cm){
36614         if(this.ds){
36615             this.ds.un("load", this.onLoad, this);
36616             this.ds.un("datachanged", this.onDataChange, this);
36617             this.ds.un("add", this.onAdd, this);
36618             this.ds.un("remove", this.onRemove, this);
36619             this.ds.un("update", this.onUpdate, this);
36620             this.ds.un("clear", this.onClear, this);
36621         }
36622         if(ds){
36623             ds.on("load", this.onLoad, this);
36624             ds.on("datachanged", this.onDataChange, this);
36625             ds.on("add", this.onAdd, this);
36626             ds.on("remove", this.onRemove, this);
36627             ds.on("update", this.onUpdate, this);
36628             ds.on("clear", this.onClear, this);
36629         }
36630         this.ds = ds;
36631
36632         if(this.cm){
36633             this.cm.un("widthchange", this.onColWidthChange, this);
36634             this.cm.un("headerchange", this.onHeaderChange, this);
36635             this.cm.un("hiddenchange", this.onHiddenChange, this);
36636             this.cm.un("columnmoved", this.onColumnMove, this);
36637             this.cm.un("columnlockchange", this.onColumnLock, this);
36638         }
36639         if(cm){
36640             this.generateRules(cm);
36641             cm.on("widthchange", this.onColWidthChange, this);
36642             cm.on("headerchange", this.onHeaderChange, this);
36643             cm.on("hiddenchange", this.onHiddenChange, this);
36644             cm.on("columnmoved", this.onColumnMove, this);
36645             cm.on("columnlockchange", this.onColumnLock, this);
36646         }
36647         this.cm = cm;
36648     },
36649
36650     init: function(grid){
36651         Roo.grid.GridView.superclass.init.call(this, grid);
36652
36653         this.bind(grid.dataSource, grid.colModel);
36654
36655         grid.on("headerclick", this.handleHeaderClick, this);
36656
36657         if(grid.trackMouseOver){
36658             grid.on("mouseover", this.onRowOver, this);
36659             grid.on("mouseout", this.onRowOut, this);
36660         }
36661         grid.cancelTextSelection = function(){};
36662         this.gridId = grid.id;
36663
36664         var tpls = this.templates || {};
36665
36666         if(!tpls.master){
36667             tpls.master = new Roo.Template(
36668                '<div class="x-grid" hidefocus="true">',
36669                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36670                   '<div class="x-grid-topbar"></div>',
36671                   '<div class="x-grid-scroller"><div></div></div>',
36672                   '<div class="x-grid-locked">',
36673                       '<div class="x-grid-header">{lockedHeader}</div>',
36674                       '<div class="x-grid-body">{lockedBody}</div>',
36675                   "</div>",
36676                   '<div class="x-grid-viewport">',
36677                       '<div class="x-grid-header">{header}</div>',
36678                       '<div class="x-grid-body">{body}</div>',
36679                   "</div>",
36680                   '<div class="x-grid-bottombar"></div>',
36681                  
36682                   '<div class="x-grid-resize-proxy">&#160;</div>',
36683                "</div>"
36684             );
36685             tpls.master.disableformats = true;
36686         }
36687
36688         if(!tpls.header){
36689             tpls.header = new Roo.Template(
36690                '<table border="0" cellspacing="0" cellpadding="0">',
36691                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36692                "</table>{splits}"
36693             );
36694             tpls.header.disableformats = true;
36695         }
36696         tpls.header.compile();
36697
36698         if(!tpls.hcell){
36699             tpls.hcell = new Roo.Template(
36700                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36701                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36702                 "</div></td>"
36703              );
36704              tpls.hcell.disableFormats = true;
36705         }
36706         tpls.hcell.compile();
36707
36708         if(!tpls.hsplit){
36709             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36710                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36711             tpls.hsplit.disableFormats = true;
36712         }
36713         tpls.hsplit.compile();
36714
36715         if(!tpls.body){
36716             tpls.body = new Roo.Template(
36717                '<table border="0" cellspacing="0" cellpadding="0">',
36718                "<tbody>{rows}</tbody>",
36719                "</table>"
36720             );
36721             tpls.body.disableFormats = true;
36722         }
36723         tpls.body.compile();
36724
36725         if(!tpls.row){
36726             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36727             tpls.row.disableFormats = true;
36728         }
36729         tpls.row.compile();
36730
36731         if(!tpls.cell){
36732             tpls.cell = new Roo.Template(
36733                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36734                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36735                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36736                 "</td>"
36737             );
36738             tpls.cell.disableFormats = true;
36739         }
36740         tpls.cell.compile();
36741
36742         this.templates = tpls;
36743     },
36744
36745     // remap these for backwards compat
36746     onColWidthChange : function(){
36747         this.updateColumns.apply(this, arguments);
36748     },
36749     onHeaderChange : function(){
36750         this.updateHeaders.apply(this, arguments);
36751     }, 
36752     onHiddenChange : function(){
36753         this.handleHiddenChange.apply(this, arguments);
36754     },
36755     onColumnMove : function(){
36756         this.handleColumnMove.apply(this, arguments);
36757     },
36758     onColumnLock : function(){
36759         this.handleLockChange.apply(this, arguments);
36760     },
36761
36762     onDataChange : function(){
36763         this.refresh();
36764         this.updateHeaderSortState();
36765     },
36766
36767     onClear : function(){
36768         this.refresh();
36769     },
36770
36771     onUpdate : function(ds, record){
36772         this.refreshRow(record);
36773     },
36774
36775     refreshRow : function(record){
36776         var ds = this.ds, index;
36777         if(typeof record == 'number'){
36778             index = record;
36779             record = ds.getAt(index);
36780         }else{
36781             index = ds.indexOf(record);
36782         }
36783         this.insertRows(ds, index, index, true);
36784         this.onRemove(ds, record, index+1, true);
36785         this.syncRowHeights(index, index);
36786         this.layout();
36787         this.fireEvent("rowupdated", this, index, record);
36788     },
36789
36790     onAdd : function(ds, records, index){
36791         this.insertRows(ds, index, index + (records.length-1));
36792     },
36793
36794     onRemove : function(ds, record, index, isUpdate){
36795         if(isUpdate !== true){
36796             this.fireEvent("beforerowremoved", this, index, record);
36797         }
36798         var bt = this.getBodyTable(), lt = this.getLockedTable();
36799         if(bt.rows[index]){
36800             bt.firstChild.removeChild(bt.rows[index]);
36801         }
36802         if(lt.rows[index]){
36803             lt.firstChild.removeChild(lt.rows[index]);
36804         }
36805         if(isUpdate !== true){
36806             this.stripeRows(index);
36807             this.syncRowHeights(index, index);
36808             this.layout();
36809             this.fireEvent("rowremoved", this, index, record);
36810         }
36811     },
36812
36813     onLoad : function(){
36814         this.scrollToTop();
36815     },
36816
36817     /**
36818      * Scrolls the grid to the top
36819      */
36820     scrollToTop : function(){
36821         if(this.scroller){
36822             this.scroller.dom.scrollTop = 0;
36823             this.syncScroll();
36824         }
36825     },
36826
36827     /**
36828      * Gets a panel in the header of the grid that can be used for toolbars etc.
36829      * After modifying the contents of this panel a call to grid.autoSize() may be
36830      * required to register any changes in size.
36831      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36832      * @return Roo.Element
36833      */
36834     getHeaderPanel : function(doShow){
36835         if(doShow){
36836             this.headerPanel.show();
36837         }
36838         return this.headerPanel;
36839     },
36840
36841     /**
36842      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36843      * After modifying the contents of this panel a call to grid.autoSize() may be
36844      * required to register any changes in size.
36845      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36846      * @return Roo.Element
36847      */
36848     getFooterPanel : function(doShow){
36849         if(doShow){
36850             this.footerPanel.show();
36851         }
36852         return this.footerPanel;
36853     },
36854
36855     initElements : function(){
36856         var E = Roo.Element;
36857         var el = this.grid.getGridEl().dom.firstChild;
36858         var cs = el.childNodes;
36859
36860         this.el = new E(el);
36861         
36862          this.focusEl = new E(el.firstChild);
36863         this.focusEl.swallowEvent("click", true);
36864         
36865         this.headerPanel = new E(cs[1]);
36866         this.headerPanel.enableDisplayMode("block");
36867
36868         this.scroller = new E(cs[2]);
36869         this.scrollSizer = new E(this.scroller.dom.firstChild);
36870
36871         this.lockedWrap = new E(cs[3]);
36872         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36873         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36874
36875         this.mainWrap = new E(cs[4]);
36876         this.mainHd = new E(this.mainWrap.dom.firstChild);
36877         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36878
36879         this.footerPanel = new E(cs[5]);
36880         this.footerPanel.enableDisplayMode("block");
36881
36882         this.resizeProxy = new E(cs[6]);
36883
36884         this.headerSelector = String.format(
36885            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36886            this.lockedHd.id, this.mainHd.id
36887         );
36888
36889         this.splitterSelector = String.format(
36890            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36891            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36892         );
36893     },
36894     idToCssName : function(s)
36895     {
36896         return s.replace(/[^a-z0-9]+/ig, '-');
36897     },
36898
36899     getHeaderCell : function(index){
36900         return Roo.DomQuery.select(this.headerSelector)[index];
36901     },
36902
36903     getHeaderCellMeasure : function(index){
36904         return this.getHeaderCell(index).firstChild;
36905     },
36906
36907     getHeaderCellText : function(index){
36908         return this.getHeaderCell(index).firstChild.firstChild;
36909     },
36910
36911     getLockedTable : function(){
36912         return this.lockedBody.dom.firstChild;
36913     },
36914
36915     getBodyTable : function(){
36916         return this.mainBody.dom.firstChild;
36917     },
36918
36919     getLockedRow : function(index){
36920         return this.getLockedTable().rows[index];
36921     },
36922
36923     getRow : function(index){
36924         return this.getBodyTable().rows[index];
36925     },
36926
36927     getRowComposite : function(index){
36928         if(!this.rowEl){
36929             this.rowEl = new Roo.CompositeElementLite();
36930         }
36931         var els = [], lrow, mrow;
36932         if(lrow = this.getLockedRow(index)){
36933             els.push(lrow);
36934         }
36935         if(mrow = this.getRow(index)){
36936             els.push(mrow);
36937         }
36938         this.rowEl.elements = els;
36939         return this.rowEl;
36940     },
36941     /**
36942      * Gets the 'td' of the cell
36943      * 
36944      * @param {Integer} rowIndex row to select
36945      * @param {Integer} colIndex column to select
36946      * 
36947      * @return {Object} 
36948      */
36949     getCell : function(rowIndex, colIndex){
36950         var locked = this.cm.getLockedCount();
36951         var source;
36952         if(colIndex < locked){
36953             source = this.lockedBody.dom.firstChild;
36954         }else{
36955             source = this.mainBody.dom.firstChild;
36956             colIndex -= locked;
36957         }
36958         return source.rows[rowIndex].childNodes[colIndex];
36959     },
36960
36961     getCellText : function(rowIndex, colIndex){
36962         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36963     },
36964
36965     getCellBox : function(cell){
36966         var b = this.fly(cell).getBox();
36967         if(Roo.isOpera){ // opera fails to report the Y
36968             b.y = cell.offsetTop + this.mainBody.getY();
36969         }
36970         return b;
36971     },
36972
36973     getCellIndex : function(cell){
36974         var id = String(cell.className).match(this.cellRE);
36975         if(id){
36976             return parseInt(id[1], 10);
36977         }
36978         return 0;
36979     },
36980
36981     findHeaderIndex : function(n){
36982         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36983         return r ? this.getCellIndex(r) : false;
36984     },
36985
36986     findHeaderCell : function(n){
36987         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36988         return r ? r : false;
36989     },
36990
36991     findRowIndex : function(n){
36992         if(!n){
36993             return false;
36994         }
36995         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36996         return r ? r.rowIndex : false;
36997     },
36998
36999     findCellIndex : function(node){
37000         var stop = this.el.dom;
37001         while(node && node != stop){
37002             if(this.findRE.test(node.className)){
37003                 return this.getCellIndex(node);
37004             }
37005             node = node.parentNode;
37006         }
37007         return false;
37008     },
37009
37010     getColumnId : function(index){
37011         return this.cm.getColumnId(index);
37012     },
37013
37014     getSplitters : function()
37015     {
37016         if(this.splitterSelector){
37017            return Roo.DomQuery.select(this.splitterSelector);
37018         }else{
37019             return null;
37020       }
37021     },
37022
37023     getSplitter : function(index){
37024         return this.getSplitters()[index];
37025     },
37026
37027     onRowOver : function(e, t){
37028         var row;
37029         if((row = this.findRowIndex(t)) !== false){
37030             this.getRowComposite(row).addClass("x-grid-row-over");
37031         }
37032     },
37033
37034     onRowOut : function(e, t){
37035         var row;
37036         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37037             this.getRowComposite(row).removeClass("x-grid-row-over");
37038         }
37039     },
37040
37041     renderHeaders : function(){
37042         var cm = this.cm;
37043         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37044         var cb = [], lb = [], sb = [], lsb = [], p = {};
37045         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37046             p.cellId = "x-grid-hd-0-" + i;
37047             p.splitId = "x-grid-csplit-0-" + i;
37048             p.id = cm.getColumnId(i);
37049             p.title = cm.getColumnTooltip(i) || "";
37050             p.value = cm.getColumnHeader(i) || "";
37051             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37052             if(!cm.isLocked(i)){
37053                 cb[cb.length] = ct.apply(p);
37054                 sb[sb.length] = st.apply(p);
37055             }else{
37056                 lb[lb.length] = ct.apply(p);
37057                 lsb[lsb.length] = st.apply(p);
37058             }
37059         }
37060         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37061                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37062     },
37063
37064     updateHeaders : function(){
37065         var html = this.renderHeaders();
37066         this.lockedHd.update(html[0]);
37067         this.mainHd.update(html[1]);
37068     },
37069
37070     /**
37071      * Focuses the specified row.
37072      * @param {Number} row The row index
37073      */
37074     focusRow : function(row)
37075     {
37076         //Roo.log('GridView.focusRow');
37077         var x = this.scroller.dom.scrollLeft;
37078         this.focusCell(row, 0, false);
37079         this.scroller.dom.scrollLeft = x;
37080     },
37081
37082     /**
37083      * Focuses the specified cell.
37084      * @param {Number} row The row index
37085      * @param {Number} col The column index
37086      * @param {Boolean} hscroll false to disable horizontal scrolling
37087      */
37088     focusCell : function(row, col, hscroll)
37089     {
37090         //Roo.log('GridView.focusCell');
37091         var el = this.ensureVisible(row, col, hscroll);
37092         this.focusEl.alignTo(el, "tl-tl");
37093         if(Roo.isGecko){
37094             this.focusEl.focus();
37095         }else{
37096             this.focusEl.focus.defer(1, this.focusEl);
37097         }
37098     },
37099
37100     /**
37101      * Scrolls the specified cell into view
37102      * @param {Number} row The row index
37103      * @param {Number} col The column index
37104      * @param {Boolean} hscroll false to disable horizontal scrolling
37105      */
37106     ensureVisible : function(row, col, hscroll)
37107     {
37108         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37109         //return null; //disable for testing.
37110         if(typeof row != "number"){
37111             row = row.rowIndex;
37112         }
37113         if(row < 0 && row >= this.ds.getCount()){
37114             return  null;
37115         }
37116         col = (col !== undefined ? col : 0);
37117         var cm = this.grid.colModel;
37118         while(cm.isHidden(col)){
37119             col++;
37120         }
37121
37122         var el = this.getCell(row, col);
37123         if(!el){
37124             return null;
37125         }
37126         var c = this.scroller.dom;
37127
37128         var ctop = parseInt(el.offsetTop, 10);
37129         var cleft = parseInt(el.offsetLeft, 10);
37130         var cbot = ctop + el.offsetHeight;
37131         var cright = cleft + el.offsetWidth;
37132         
37133         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37134         var stop = parseInt(c.scrollTop, 10);
37135         var sleft = parseInt(c.scrollLeft, 10);
37136         var sbot = stop + ch;
37137         var sright = sleft + c.clientWidth;
37138         /*
37139         Roo.log('GridView.ensureVisible:' +
37140                 ' ctop:' + ctop +
37141                 ' c.clientHeight:' + c.clientHeight +
37142                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37143                 ' stop:' + stop +
37144                 ' cbot:' + cbot +
37145                 ' sbot:' + sbot +
37146                 ' ch:' + ch  
37147                 );
37148         */
37149         if(ctop < stop){
37150              c.scrollTop = ctop;
37151             //Roo.log("set scrolltop to ctop DISABLE?");
37152         }else if(cbot > sbot){
37153             //Roo.log("set scrolltop to cbot-ch");
37154             c.scrollTop = cbot-ch;
37155         }
37156         
37157         if(hscroll !== false){
37158             if(cleft < sleft){
37159                 c.scrollLeft = cleft;
37160             }else if(cright > sright){
37161                 c.scrollLeft = cright-c.clientWidth;
37162             }
37163         }
37164          
37165         return el;
37166     },
37167
37168     updateColumns : function(){
37169         this.grid.stopEditing();
37170         var cm = this.grid.colModel, colIds = this.getColumnIds();
37171         //var totalWidth = cm.getTotalWidth();
37172         var pos = 0;
37173         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37174             //if(cm.isHidden(i)) continue;
37175             var w = cm.getColumnWidth(i);
37176             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37177             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37178         }
37179         this.updateSplitters();
37180     },
37181
37182     generateRules : function(cm){
37183         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37184         Roo.util.CSS.removeStyleSheet(rulesId);
37185         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37186             var cid = cm.getColumnId(i);
37187             var align = '';
37188             if(cm.config[i].align){
37189                 align = 'text-align:'+cm.config[i].align+';';
37190             }
37191             var hidden = '';
37192             if(cm.isHidden(i)){
37193                 hidden = 'display:none;';
37194             }
37195             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37196             ruleBuf.push(
37197                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37198                     this.hdSelector, cid, " {\n", align, width, "}\n",
37199                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37200                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37201         }
37202         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37203     },
37204
37205     updateSplitters : function(){
37206         var cm = this.cm, s = this.getSplitters();
37207         if(s){ // splitters not created yet
37208             var pos = 0, locked = true;
37209             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37210                 if(cm.isHidden(i)) continue;
37211                 var w = cm.getColumnWidth(i); // make sure it's a number
37212                 if(!cm.isLocked(i) && locked){
37213                     pos = 0;
37214                     locked = false;
37215                 }
37216                 pos += w;
37217                 s[i].style.left = (pos-this.splitOffset) + "px";
37218             }
37219         }
37220     },
37221
37222     handleHiddenChange : function(colModel, colIndex, hidden){
37223         if(hidden){
37224             this.hideColumn(colIndex);
37225         }else{
37226             this.unhideColumn(colIndex);
37227         }
37228     },
37229
37230     hideColumn : function(colIndex){
37231         var cid = this.getColumnId(colIndex);
37232         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37233         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37234         if(Roo.isSafari){
37235             this.updateHeaders();
37236         }
37237         this.updateSplitters();
37238         this.layout();
37239     },
37240
37241     unhideColumn : function(colIndex){
37242         var cid = this.getColumnId(colIndex);
37243         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37244         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37245
37246         if(Roo.isSafari){
37247             this.updateHeaders();
37248         }
37249         this.updateSplitters();
37250         this.layout();
37251     },
37252
37253     insertRows : function(dm, firstRow, lastRow, isUpdate){
37254         if(firstRow == 0 && lastRow == dm.getCount()-1){
37255             this.refresh();
37256         }else{
37257             if(!isUpdate){
37258                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37259             }
37260             var s = this.getScrollState();
37261             var markup = this.renderRows(firstRow, lastRow);
37262             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37263             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37264             this.restoreScroll(s);
37265             if(!isUpdate){
37266                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37267                 this.syncRowHeights(firstRow, lastRow);
37268                 this.stripeRows(firstRow);
37269                 this.layout();
37270             }
37271         }
37272     },
37273
37274     bufferRows : function(markup, target, index){
37275         var before = null, trows = target.rows, tbody = target.tBodies[0];
37276         if(index < trows.length){
37277             before = trows[index];
37278         }
37279         var b = document.createElement("div");
37280         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37281         var rows = b.firstChild.rows;
37282         for(var i = 0, len = rows.length; i < len; i++){
37283             if(before){
37284                 tbody.insertBefore(rows[0], before);
37285             }else{
37286                 tbody.appendChild(rows[0]);
37287             }
37288         }
37289         b.innerHTML = "";
37290         b = null;
37291     },
37292
37293     deleteRows : function(dm, firstRow, lastRow){
37294         if(dm.getRowCount()<1){
37295             this.fireEvent("beforerefresh", this);
37296             this.mainBody.update("");
37297             this.lockedBody.update("");
37298             this.fireEvent("refresh", this);
37299         }else{
37300             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37301             var bt = this.getBodyTable();
37302             var tbody = bt.firstChild;
37303             var rows = bt.rows;
37304             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37305                 tbody.removeChild(rows[firstRow]);
37306             }
37307             this.stripeRows(firstRow);
37308             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37309         }
37310     },
37311
37312     updateRows : function(dataSource, firstRow, lastRow){
37313         var s = this.getScrollState();
37314         this.refresh();
37315         this.restoreScroll(s);
37316     },
37317
37318     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37319         if(!noRefresh){
37320            this.refresh();
37321         }
37322         this.updateHeaderSortState();
37323     },
37324
37325     getScrollState : function(){
37326         
37327         var sb = this.scroller.dom;
37328         return {left: sb.scrollLeft, top: sb.scrollTop};
37329     },
37330
37331     stripeRows : function(startRow){
37332         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37333             return;
37334         }
37335         startRow = startRow || 0;
37336         var rows = this.getBodyTable().rows;
37337         var lrows = this.getLockedTable().rows;
37338         var cls = ' x-grid-row-alt ';
37339         for(var i = startRow, len = rows.length; i < len; i++){
37340             var row = rows[i], lrow = lrows[i];
37341             var isAlt = ((i+1) % 2 == 0);
37342             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37343             if(isAlt == hasAlt){
37344                 continue;
37345             }
37346             if(isAlt){
37347                 row.className += " x-grid-row-alt";
37348             }else{
37349                 row.className = row.className.replace("x-grid-row-alt", "");
37350             }
37351             if(lrow){
37352                 lrow.className = row.className;
37353             }
37354         }
37355     },
37356
37357     restoreScroll : function(state){
37358         //Roo.log('GridView.restoreScroll');
37359         var sb = this.scroller.dom;
37360         sb.scrollLeft = state.left;
37361         sb.scrollTop = state.top;
37362         this.syncScroll();
37363     },
37364
37365     syncScroll : function(){
37366         //Roo.log('GridView.syncScroll');
37367         var sb = this.scroller.dom;
37368         var sh = this.mainHd.dom;
37369         var bs = this.mainBody.dom;
37370         var lv = this.lockedBody.dom;
37371         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37372         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37373     },
37374
37375     handleScroll : function(e){
37376         this.syncScroll();
37377         var sb = this.scroller.dom;
37378         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37379         e.stopEvent();
37380     },
37381
37382     handleWheel : function(e){
37383         var d = e.getWheelDelta();
37384         this.scroller.dom.scrollTop -= d*22;
37385         // set this here to prevent jumpy scrolling on large tables
37386         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37387         e.stopEvent();
37388     },
37389
37390     renderRows : function(startRow, endRow){
37391         // pull in all the crap needed to render rows
37392         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37393         var colCount = cm.getColumnCount();
37394
37395         if(ds.getCount() < 1){
37396             return ["", ""];
37397         }
37398
37399         // build a map for all the columns
37400         var cs = [];
37401         for(var i = 0; i < colCount; i++){
37402             var name = cm.getDataIndex(i);
37403             cs[i] = {
37404                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37405                 renderer : cm.getRenderer(i),
37406                 id : cm.getColumnId(i),
37407                 locked : cm.isLocked(i)
37408             };
37409         }
37410
37411         startRow = startRow || 0;
37412         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37413
37414         // records to render
37415         var rs = ds.getRange(startRow, endRow);
37416
37417         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37418     },
37419
37420     // As much as I hate to duplicate code, this was branched because FireFox really hates
37421     // [].join("") on strings. The performance difference was substantial enough to
37422     // branch this function
37423     doRender : Roo.isGecko ?
37424             function(cs, rs, ds, startRow, colCount, stripe){
37425                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37426                 // buffers
37427                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37428                 
37429                 var hasListener = this.grid.hasListener('rowclass');
37430                 var rowcfg = {};
37431                 for(var j = 0, len = rs.length; j < len; j++){
37432                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37433                     for(var i = 0; i < colCount; i++){
37434                         c = cs[i];
37435                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37436                         p.id = c.id;
37437                         p.css = p.attr = "";
37438                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37439                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37440                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37441                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37442                         }
37443                         var markup = ct.apply(p);
37444                         if(!c.locked){
37445                             cb+= markup;
37446                         }else{
37447                             lcb+= markup;
37448                         }
37449                     }
37450                     var alt = [];
37451                     if(stripe && ((rowIndex+1) % 2 == 0)){
37452                         alt.push("x-grid-row-alt")
37453                     }
37454                     if(r.dirty){
37455                         alt.push(  " x-grid-dirty-row");
37456                     }
37457                     rp.cells = lcb;
37458                     if(this.getRowClass){
37459                         alt.push(this.getRowClass(r, rowIndex));
37460                     }
37461                     if (hasListener) {
37462                         rowcfg = {
37463                              
37464                             record: r,
37465                             rowIndex : rowIndex,
37466                             rowClass : ''
37467                         }
37468                         this.grid.fireEvent('rowclass', this, rowcfg);
37469                         alt.push(rowcfg.rowClass);
37470                     }
37471                     rp.alt = alt.join(" ");
37472                     lbuf+= rt.apply(rp);
37473                     rp.cells = cb;
37474                     buf+=  rt.apply(rp);
37475                 }
37476                 return [lbuf, buf];
37477             } :
37478             function(cs, rs, ds, startRow, colCount, stripe){
37479                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37480                 // buffers
37481                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37482                 var hasListener = this.grid.hasListener('rowclass');
37483  
37484                 var rowcfg = {};
37485                 for(var j = 0, len = rs.length; j < len; j++){
37486                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37487                     for(var i = 0; i < colCount; i++){
37488                         c = cs[i];
37489                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37490                         p.id = c.id;
37491                         p.css = p.attr = "";
37492                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37493                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37494                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37495                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37496                         }
37497                         
37498                         var markup = ct.apply(p);
37499                         if(!c.locked){
37500                             cb[cb.length] = markup;
37501                         }else{
37502                             lcb[lcb.length] = markup;
37503                         }
37504                     }
37505                     var alt = [];
37506                     if(stripe && ((rowIndex+1) % 2 == 0)){
37507                         alt.push( "x-grid-row-alt");
37508                     }
37509                     if(r.dirty){
37510                         alt.push(" x-grid-dirty-row");
37511                     }
37512                     rp.cells = lcb;
37513                     if(this.getRowClass){
37514                         alt.push( this.getRowClass(r, rowIndex));
37515                     }
37516                     if (hasListener) {
37517                         rowcfg = {
37518                              
37519                             record: r,
37520                             rowIndex : rowIndex,
37521                             rowClass : ''
37522                         }
37523                         this.grid.fireEvent('rowclass', this, rowcfg);
37524                         alt.push(rowcfg.rowClass);
37525                     }
37526                     rp.alt = alt.join(" ");
37527                     rp.cells = lcb.join("");
37528                     lbuf[lbuf.length] = rt.apply(rp);
37529                     rp.cells = cb.join("");
37530                     buf[buf.length] =  rt.apply(rp);
37531                 }
37532                 return [lbuf.join(""), buf.join("")];
37533             },
37534
37535     renderBody : function(){
37536         var markup = this.renderRows();
37537         var bt = this.templates.body;
37538         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37539     },
37540
37541     /**
37542      * Refreshes the grid
37543      * @param {Boolean} headersToo
37544      */
37545     refresh : function(headersToo){
37546         this.fireEvent("beforerefresh", this);
37547         this.grid.stopEditing();
37548         var result = this.renderBody();
37549         this.lockedBody.update(result[0]);
37550         this.mainBody.update(result[1]);
37551         if(headersToo === true){
37552             this.updateHeaders();
37553             this.updateColumns();
37554             this.updateSplitters();
37555             this.updateHeaderSortState();
37556         }
37557         this.syncRowHeights();
37558         this.layout();
37559         this.fireEvent("refresh", this);
37560     },
37561
37562     handleColumnMove : function(cm, oldIndex, newIndex){
37563         this.indexMap = null;
37564         var s = this.getScrollState();
37565         this.refresh(true);
37566         this.restoreScroll(s);
37567         this.afterMove(newIndex);
37568     },
37569
37570     afterMove : function(colIndex){
37571         if(this.enableMoveAnim && Roo.enableFx){
37572             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37573         }
37574         // if multisort - fix sortOrder, and reload..
37575         if (this.grid.dataSource.multiSort) {
37576             // the we can call sort again..
37577             var dm = this.grid.dataSource;
37578             var cm = this.grid.colModel;
37579             var so = [];
37580             for(var i = 0; i < cm.config.length; i++ ) {
37581                 
37582                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37583                     continue; // dont' bother, it's not in sort list or being set.
37584                 }
37585                 
37586                 so.push(cm.config[i].dataIndex);
37587             };
37588             dm.sortOrder = so;
37589             dm.load(dm.lastOptions);
37590             
37591             
37592         }
37593         
37594     },
37595
37596     updateCell : function(dm, rowIndex, dataIndex){
37597         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37598         if(typeof colIndex == "undefined"){ // not present in grid
37599             return;
37600         }
37601         var cm = this.grid.colModel;
37602         var cell = this.getCell(rowIndex, colIndex);
37603         var cellText = this.getCellText(rowIndex, colIndex);
37604
37605         var p = {
37606             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37607             id : cm.getColumnId(colIndex),
37608             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37609         };
37610         var renderer = cm.getRenderer(colIndex);
37611         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37612         if(typeof val == "undefined" || val === "") val = "&#160;";
37613         cellText.innerHTML = val;
37614         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37615         this.syncRowHeights(rowIndex, rowIndex);
37616     },
37617
37618     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37619         var maxWidth = 0;
37620         if(this.grid.autoSizeHeaders){
37621             var h = this.getHeaderCellMeasure(colIndex);
37622             maxWidth = Math.max(maxWidth, h.scrollWidth);
37623         }
37624         var tb, index;
37625         if(this.cm.isLocked(colIndex)){
37626             tb = this.getLockedTable();
37627             index = colIndex;
37628         }else{
37629             tb = this.getBodyTable();
37630             index = colIndex - this.cm.getLockedCount();
37631         }
37632         if(tb && tb.rows){
37633             var rows = tb.rows;
37634             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37635             for(var i = 0; i < stopIndex; i++){
37636                 var cell = rows[i].childNodes[index].firstChild;
37637                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37638             }
37639         }
37640         return maxWidth + /*margin for error in IE*/ 5;
37641     },
37642     /**
37643      * Autofit a column to its content.
37644      * @param {Number} colIndex
37645      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37646      */
37647      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37648          if(this.cm.isHidden(colIndex)){
37649              return; // can't calc a hidden column
37650          }
37651         if(forceMinSize){
37652             var cid = this.cm.getColumnId(colIndex);
37653             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37654            if(this.grid.autoSizeHeaders){
37655                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37656            }
37657         }
37658         var newWidth = this.calcColumnWidth(colIndex);
37659         this.cm.setColumnWidth(colIndex,
37660             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37661         if(!suppressEvent){
37662             this.grid.fireEvent("columnresize", colIndex, newWidth);
37663         }
37664     },
37665
37666     /**
37667      * Autofits all columns to their content and then expands to fit any extra space in the grid
37668      */
37669      autoSizeColumns : function(){
37670         var cm = this.grid.colModel;
37671         var colCount = cm.getColumnCount();
37672         for(var i = 0; i < colCount; i++){
37673             this.autoSizeColumn(i, true, true);
37674         }
37675         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37676             this.fitColumns();
37677         }else{
37678             this.updateColumns();
37679             this.layout();
37680         }
37681     },
37682
37683     /**
37684      * Autofits all columns to the grid's width proportionate with their current size
37685      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37686      */
37687     fitColumns : function(reserveScrollSpace){
37688         var cm = this.grid.colModel;
37689         var colCount = cm.getColumnCount();
37690         var cols = [];
37691         var width = 0;
37692         var i, w;
37693         for (i = 0; i < colCount; i++){
37694             if(!cm.isHidden(i) && !cm.isFixed(i)){
37695                 w = cm.getColumnWidth(i);
37696                 cols.push(i);
37697                 cols.push(w);
37698                 width += w;
37699             }
37700         }
37701         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37702         if(reserveScrollSpace){
37703             avail -= 17;
37704         }
37705         var frac = (avail - cm.getTotalWidth())/width;
37706         while (cols.length){
37707             w = cols.pop();
37708             i = cols.pop();
37709             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37710         }
37711         this.updateColumns();
37712         this.layout();
37713     },
37714
37715     onRowSelect : function(rowIndex){
37716         var row = this.getRowComposite(rowIndex);
37717         row.addClass("x-grid-row-selected");
37718     },
37719
37720     onRowDeselect : function(rowIndex){
37721         var row = this.getRowComposite(rowIndex);
37722         row.removeClass("x-grid-row-selected");
37723     },
37724
37725     onCellSelect : function(row, col){
37726         var cell = this.getCell(row, col);
37727         if(cell){
37728             Roo.fly(cell).addClass("x-grid-cell-selected");
37729         }
37730     },
37731
37732     onCellDeselect : function(row, col){
37733         var cell = this.getCell(row, col);
37734         if(cell){
37735             Roo.fly(cell).removeClass("x-grid-cell-selected");
37736         }
37737     },
37738
37739     updateHeaderSortState : function(){
37740         
37741         // sort state can be single { field: xxx, direction : yyy}
37742         // or   { xxx=>ASC , yyy : DESC ..... }
37743         
37744         var mstate = {};
37745         if (!this.ds.multiSort) { 
37746             var state = this.ds.getSortState();
37747             if(!state){
37748                 return;
37749             }
37750             mstate[state.field] = state.direction;
37751             // FIXME... - this is not used here.. but might be elsewhere..
37752             this.sortState = state;
37753             
37754         } else {
37755             mstate = this.ds.sortToggle;
37756         }
37757         //remove existing sort classes..
37758         
37759         var sc = this.sortClasses;
37760         var hds = this.el.select(this.headerSelector).removeClass(sc);
37761         
37762         for(var f in mstate) {
37763         
37764             var sortColumn = this.cm.findColumnIndex(f);
37765             
37766             if(sortColumn != -1){
37767                 var sortDir = mstate[f];        
37768                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37769             }
37770         }
37771         
37772          
37773         
37774     },
37775
37776
37777     handleHeaderClick : function(g, index,e){
37778         
37779         Roo.log("header click");
37780         
37781         if (Roo.isTouch) {
37782             // touch events on header are handled by context
37783             this.handleHdCtx(g,index,e);
37784             return;
37785         }
37786         
37787         
37788         if(this.headersDisabled){
37789             return;
37790         }
37791         var dm = g.dataSource, cm = g.colModel;
37792         if(!cm.isSortable(index)){
37793             return;
37794         }
37795         g.stopEditing();
37796         
37797         if (dm.multiSort) {
37798             // update the sortOrder
37799             var so = [];
37800             for(var i = 0; i < cm.config.length; i++ ) {
37801                 
37802                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37803                     continue; // dont' bother, it's not in sort list or being set.
37804                 }
37805                 
37806                 so.push(cm.config[i].dataIndex);
37807             };
37808             dm.sortOrder = so;
37809         }
37810         
37811         
37812         dm.sort(cm.getDataIndex(index));
37813     },
37814
37815
37816     destroy : function(){
37817         if(this.colMenu){
37818             this.colMenu.removeAll();
37819             Roo.menu.MenuMgr.unregister(this.colMenu);
37820             this.colMenu.getEl().remove();
37821             delete this.colMenu;
37822         }
37823         if(this.hmenu){
37824             this.hmenu.removeAll();
37825             Roo.menu.MenuMgr.unregister(this.hmenu);
37826             this.hmenu.getEl().remove();
37827             delete this.hmenu;
37828         }
37829         if(this.grid.enableColumnMove){
37830             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37831             if(dds){
37832                 for(var dd in dds){
37833                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37834                         var elid = dds[dd].dragElId;
37835                         dds[dd].unreg();
37836                         Roo.get(elid).remove();
37837                     } else if(dds[dd].config.isTarget){
37838                         dds[dd].proxyTop.remove();
37839                         dds[dd].proxyBottom.remove();
37840                         dds[dd].unreg();
37841                     }
37842                     if(Roo.dd.DDM.locationCache[dd]){
37843                         delete Roo.dd.DDM.locationCache[dd];
37844                     }
37845                 }
37846                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37847             }
37848         }
37849         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37850         this.bind(null, null);
37851         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37852     },
37853
37854     handleLockChange : function(){
37855         this.refresh(true);
37856     },
37857
37858     onDenyColumnLock : function(){
37859
37860     },
37861
37862     onDenyColumnHide : function(){
37863
37864     },
37865
37866     handleHdMenuClick : function(item){
37867         var index = this.hdCtxIndex;
37868         var cm = this.cm, ds = this.ds;
37869         switch(item.id){
37870             case "asc":
37871                 ds.sort(cm.getDataIndex(index), "ASC");
37872                 break;
37873             case "desc":
37874                 ds.sort(cm.getDataIndex(index), "DESC");
37875                 break;
37876             case "lock":
37877                 var lc = cm.getLockedCount();
37878                 if(cm.getColumnCount(true) <= lc+1){
37879                     this.onDenyColumnLock();
37880                     return;
37881                 }
37882                 if(lc != index){
37883                     cm.setLocked(index, true, true);
37884                     cm.moveColumn(index, lc);
37885                     this.grid.fireEvent("columnmove", index, lc);
37886                 }else{
37887                     cm.setLocked(index, true);
37888                 }
37889             break;
37890             case "unlock":
37891                 var lc = cm.getLockedCount();
37892                 if((lc-1) != index){
37893                     cm.setLocked(index, false, true);
37894                     cm.moveColumn(index, lc-1);
37895                     this.grid.fireEvent("columnmove", index, lc-1);
37896                 }else{
37897                     cm.setLocked(index, false);
37898                 }
37899             break;
37900             case 'wider': // used to expand cols on touch..
37901             case 'narrow':
37902                 var cw = cm.getColumnWidth(index);
37903                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37904                 cw = Math.max(0, cw);
37905                 cw = Math.min(cw,4000);
37906                 cm.setColumnWidth(index, cw);
37907                 break;
37908                 
37909             default:
37910                 index = cm.getIndexById(item.id.substr(4));
37911                 if(index != -1){
37912                     if(item.checked && cm.getColumnCount(true) <= 1){
37913                         this.onDenyColumnHide();
37914                         return false;
37915                     }
37916                     cm.setHidden(index, item.checked);
37917                 }
37918         }
37919         return true;
37920     },
37921
37922     beforeColMenuShow : function(){
37923         var cm = this.cm,  colCount = cm.getColumnCount();
37924         this.colMenu.removeAll();
37925         for(var i = 0; i < colCount; i++){
37926             this.colMenu.add(new Roo.menu.CheckItem({
37927                 id: "col-"+cm.getColumnId(i),
37928                 text: cm.getColumnHeader(i),
37929                 checked: !cm.isHidden(i),
37930                 hideOnClick:false
37931             }));
37932         }
37933     },
37934
37935     handleHdCtx : function(g, index, e){
37936         e.stopEvent();
37937         var hd = this.getHeaderCell(index);
37938         this.hdCtxIndex = index;
37939         var ms = this.hmenu.items, cm = this.cm;
37940         ms.get("asc").setDisabled(!cm.isSortable(index));
37941         ms.get("desc").setDisabled(!cm.isSortable(index));
37942         if(this.grid.enableColLock !== false){
37943             ms.get("lock").setDisabled(cm.isLocked(index));
37944             ms.get("unlock").setDisabled(!cm.isLocked(index));
37945         }
37946         this.hmenu.show(hd, "tl-bl");
37947     },
37948
37949     handleHdOver : function(e){
37950         var hd = this.findHeaderCell(e.getTarget());
37951         if(hd && !this.headersDisabled){
37952             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37953                this.fly(hd).addClass("x-grid-hd-over");
37954             }
37955         }
37956     },
37957
37958     handleHdOut : function(e){
37959         var hd = this.findHeaderCell(e.getTarget());
37960         if(hd){
37961             this.fly(hd).removeClass("x-grid-hd-over");
37962         }
37963     },
37964
37965     handleSplitDblClick : function(e, t){
37966         var i = this.getCellIndex(t);
37967         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37968             this.autoSizeColumn(i, true);
37969             this.layout();
37970         }
37971     },
37972
37973     render : function(){
37974
37975         var cm = this.cm;
37976         var colCount = cm.getColumnCount();
37977
37978         if(this.grid.monitorWindowResize === true){
37979             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37980         }
37981         var header = this.renderHeaders();
37982         var body = this.templates.body.apply({rows:""});
37983         var html = this.templates.master.apply({
37984             lockedBody: body,
37985             body: body,
37986             lockedHeader: header[0],
37987             header: header[1]
37988         });
37989
37990         //this.updateColumns();
37991
37992         this.grid.getGridEl().dom.innerHTML = html;
37993
37994         this.initElements();
37995         
37996         // a kludge to fix the random scolling effect in webkit
37997         this.el.on("scroll", function() {
37998             this.el.dom.scrollTop=0; // hopefully not recursive..
37999         },this);
38000
38001         this.scroller.on("scroll", this.handleScroll, this);
38002         this.lockedBody.on("mousewheel", this.handleWheel, this);
38003         this.mainBody.on("mousewheel", this.handleWheel, this);
38004
38005         this.mainHd.on("mouseover", this.handleHdOver, this);
38006         this.mainHd.on("mouseout", this.handleHdOut, this);
38007         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38008                 {delegate: "."+this.splitClass});
38009
38010         this.lockedHd.on("mouseover", this.handleHdOver, this);
38011         this.lockedHd.on("mouseout", this.handleHdOut, this);
38012         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38013                 {delegate: "."+this.splitClass});
38014
38015         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38016             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38017         }
38018
38019         this.updateSplitters();
38020
38021         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38022             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38023             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38024         }
38025
38026         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38027             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38028             this.hmenu.add(
38029                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38030                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38031             );
38032             if(this.grid.enableColLock !== false){
38033                 this.hmenu.add('-',
38034                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38035                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38036                 );
38037             }
38038             if (Roo.isTouch) {
38039                  this.hmenu.add('-',
38040                     {id:"wider", text: this.columnsWiderText},
38041                     {id:"narrow", text: this.columnsNarrowText }
38042                 );
38043                 
38044                  
38045             }
38046             
38047             if(this.grid.enableColumnHide !== false){
38048
38049                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38050                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38051                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38052
38053                 this.hmenu.add('-',
38054                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38055                 );
38056             }
38057             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38058
38059             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38060         }
38061
38062         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38063             this.dd = new Roo.grid.GridDragZone(this.grid, {
38064                 ddGroup : this.grid.ddGroup || 'GridDD'
38065             });
38066             
38067         }
38068
38069         /*
38070         for(var i = 0; i < colCount; i++){
38071             if(cm.isHidden(i)){
38072                 this.hideColumn(i);
38073             }
38074             if(cm.config[i].align){
38075                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38076                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38077             }
38078         }*/
38079         
38080         this.updateHeaderSortState();
38081
38082         this.beforeInitialResize();
38083         this.layout(true);
38084
38085         // two part rendering gives faster view to the user
38086         this.renderPhase2.defer(1, this);
38087     },
38088
38089     renderPhase2 : function(){
38090         // render the rows now
38091         this.refresh();
38092         if(this.grid.autoSizeColumns){
38093             this.autoSizeColumns();
38094         }
38095     },
38096
38097     beforeInitialResize : function(){
38098
38099     },
38100
38101     onColumnSplitterMoved : function(i, w){
38102         this.userResized = true;
38103         var cm = this.grid.colModel;
38104         cm.setColumnWidth(i, w, true);
38105         var cid = cm.getColumnId(i);
38106         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38107         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38108         this.updateSplitters();
38109         this.layout();
38110         this.grid.fireEvent("columnresize", i, w);
38111     },
38112
38113     syncRowHeights : function(startIndex, endIndex){
38114         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38115             startIndex = startIndex || 0;
38116             var mrows = this.getBodyTable().rows;
38117             var lrows = this.getLockedTable().rows;
38118             var len = mrows.length-1;
38119             endIndex = Math.min(endIndex || len, len);
38120             for(var i = startIndex; i <= endIndex; i++){
38121                 var m = mrows[i], l = lrows[i];
38122                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38123                 m.style.height = l.style.height = h + "px";
38124             }
38125         }
38126     },
38127
38128     layout : function(initialRender, is2ndPass){
38129         var g = this.grid;
38130         var auto = g.autoHeight;
38131         var scrollOffset = 16;
38132         var c = g.getGridEl(), cm = this.cm,
38133                 expandCol = g.autoExpandColumn,
38134                 gv = this;
38135         //c.beginMeasure();
38136
38137         if(!c.dom.offsetWidth){ // display:none?
38138             if(initialRender){
38139                 this.lockedWrap.show();
38140                 this.mainWrap.show();
38141             }
38142             return;
38143         }
38144
38145         var hasLock = this.cm.isLocked(0);
38146
38147         var tbh = this.headerPanel.getHeight();
38148         var bbh = this.footerPanel.getHeight();
38149
38150         if(auto){
38151             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38152             var newHeight = ch + c.getBorderWidth("tb");
38153             if(g.maxHeight){
38154                 newHeight = Math.min(g.maxHeight, newHeight);
38155             }
38156             c.setHeight(newHeight);
38157         }
38158
38159         if(g.autoWidth){
38160             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38161         }
38162
38163         var s = this.scroller;
38164
38165         var csize = c.getSize(true);
38166
38167         this.el.setSize(csize.width, csize.height);
38168
38169         this.headerPanel.setWidth(csize.width);
38170         this.footerPanel.setWidth(csize.width);
38171
38172         var hdHeight = this.mainHd.getHeight();
38173         var vw = csize.width;
38174         var vh = csize.height - (tbh + bbh);
38175
38176         s.setSize(vw, vh);
38177
38178         var bt = this.getBodyTable();
38179         var ltWidth = hasLock ?
38180                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38181
38182         var scrollHeight = bt.offsetHeight;
38183         var scrollWidth = ltWidth + bt.offsetWidth;
38184         var vscroll = false, hscroll = false;
38185
38186         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38187
38188         var lw = this.lockedWrap, mw = this.mainWrap;
38189         var lb = this.lockedBody, mb = this.mainBody;
38190
38191         setTimeout(function(){
38192             var t = s.dom.offsetTop;
38193             var w = s.dom.clientWidth,
38194                 h = s.dom.clientHeight;
38195
38196             lw.setTop(t);
38197             lw.setSize(ltWidth, h);
38198
38199             mw.setLeftTop(ltWidth, t);
38200             mw.setSize(w-ltWidth, h);
38201
38202             lb.setHeight(h-hdHeight);
38203             mb.setHeight(h-hdHeight);
38204
38205             if(is2ndPass !== true && !gv.userResized && expandCol){
38206                 // high speed resize without full column calculation
38207                 
38208                 var ci = cm.getIndexById(expandCol);
38209                 if (ci < 0) {
38210                     ci = cm.findColumnIndex(expandCol);
38211                 }
38212                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38213                 var expandId = cm.getColumnId(ci);
38214                 var  tw = cm.getTotalWidth(false);
38215                 var currentWidth = cm.getColumnWidth(ci);
38216                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38217                 if(currentWidth != cw){
38218                     cm.setColumnWidth(ci, cw, true);
38219                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38220                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38221                     gv.updateSplitters();
38222                     gv.layout(false, true);
38223                 }
38224             }
38225
38226             if(initialRender){
38227                 lw.show();
38228                 mw.show();
38229             }
38230             //c.endMeasure();
38231         }, 10);
38232     },
38233
38234     onWindowResize : function(){
38235         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38236             return;
38237         }
38238         this.layout();
38239     },
38240
38241     appendFooter : function(parentEl){
38242         return null;
38243     },
38244
38245     sortAscText : "Sort Ascending",
38246     sortDescText : "Sort Descending",
38247     lockText : "Lock Column",
38248     unlockText : "Unlock Column",
38249     columnsText : "Columns",
38250  
38251     columnsWiderText : "Wider",
38252     columnsNarrowText : "Thinner"
38253 });
38254
38255
38256 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38257     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38258     this.proxy.el.addClass('x-grid3-col-dd');
38259 };
38260
38261 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38262     handleMouseDown : function(e){
38263
38264     },
38265
38266     callHandleMouseDown : function(e){
38267         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38268     }
38269 });
38270 /*
38271  * Based on:
38272  * Ext JS Library 1.1.1
38273  * Copyright(c) 2006-2007, Ext JS, LLC.
38274  *
38275  * Originally Released Under LGPL - original licence link has changed is not relivant.
38276  *
38277  * Fork - LGPL
38278  * <script type="text/javascript">
38279  */
38280  
38281 // private
38282 // This is a support class used internally by the Grid components
38283 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38284     this.grid = grid;
38285     this.view = grid.getView();
38286     this.proxy = this.view.resizeProxy;
38287     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38288         "gridSplitters" + this.grid.getGridEl().id, {
38289         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38290     });
38291     this.setHandleElId(Roo.id(hd));
38292     this.setOuterHandleElId(Roo.id(hd2));
38293     this.scroll = false;
38294 };
38295 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38296     fly: Roo.Element.fly,
38297
38298     b4StartDrag : function(x, y){
38299         this.view.headersDisabled = true;
38300         this.proxy.setHeight(this.view.mainWrap.getHeight());
38301         var w = this.cm.getColumnWidth(this.cellIndex);
38302         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38303         this.resetConstraints();
38304         this.setXConstraint(minw, 1000);
38305         this.setYConstraint(0, 0);
38306         this.minX = x - minw;
38307         this.maxX = x + 1000;
38308         this.startPos = x;
38309         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38310     },
38311
38312
38313     handleMouseDown : function(e){
38314         ev = Roo.EventObject.setEvent(e);
38315         var t = this.fly(ev.getTarget());
38316         if(t.hasClass("x-grid-split")){
38317             this.cellIndex = this.view.getCellIndex(t.dom);
38318             this.split = t.dom;
38319             this.cm = this.grid.colModel;
38320             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38321                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38322             }
38323         }
38324     },
38325
38326     endDrag : function(e){
38327         this.view.headersDisabled = false;
38328         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38329         var diff = endX - this.startPos;
38330         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38331     },
38332
38333     autoOffset : function(){
38334         this.setDelta(0,0);
38335     }
38336 });/*
38337  * Based on:
38338  * Ext JS Library 1.1.1
38339  * Copyright(c) 2006-2007, Ext JS, LLC.
38340  *
38341  * Originally Released Under LGPL - original licence link has changed is not relivant.
38342  *
38343  * Fork - LGPL
38344  * <script type="text/javascript">
38345  */
38346  
38347 // private
38348 // This is a support class used internally by the Grid components
38349 Roo.grid.GridDragZone = function(grid, config){
38350     this.view = grid.getView();
38351     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38352     if(this.view.lockedBody){
38353         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38354         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38355     }
38356     this.scroll = false;
38357     this.grid = grid;
38358     this.ddel = document.createElement('div');
38359     this.ddel.className = 'x-grid-dd-wrap';
38360 };
38361
38362 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38363     ddGroup : "GridDD",
38364
38365     getDragData : function(e){
38366         var t = Roo.lib.Event.getTarget(e);
38367         var rowIndex = this.view.findRowIndex(t);
38368         var sm = this.grid.selModel;
38369             
38370         //Roo.log(rowIndex);
38371         
38372         if (sm.getSelectedCell) {
38373             // cell selection..
38374             if (!sm.getSelectedCell()) {
38375                 return false;
38376             }
38377             if (rowIndex != sm.getSelectedCell()[0]) {
38378                 return false;
38379             }
38380         
38381         }
38382         
38383         if(rowIndex !== false){
38384             
38385             // if editorgrid.. 
38386             
38387             
38388             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38389                
38390             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38391               //  
38392             //}
38393             if (e.hasModifier()){
38394                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38395             }
38396             
38397             Roo.log("getDragData");
38398             
38399             return {
38400                 grid: this.grid,
38401                 ddel: this.ddel,
38402                 rowIndex: rowIndex,
38403                 selections:sm.getSelections ? sm.getSelections() : (
38404                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38405                 )
38406             };
38407         }
38408         return false;
38409     },
38410
38411     onInitDrag : function(e){
38412         var data = this.dragData;
38413         this.ddel.innerHTML = this.grid.getDragDropText();
38414         this.proxy.update(this.ddel);
38415         // fire start drag?
38416     },
38417
38418     afterRepair : function(){
38419         this.dragging = false;
38420     },
38421
38422     getRepairXY : function(e, data){
38423         return false;
38424     },
38425
38426     onEndDrag : function(data, e){
38427         // fire end drag?
38428     },
38429
38430     onValidDrop : function(dd, e, id){
38431         // fire drag drop?
38432         this.hideProxy();
38433     },
38434
38435     beforeInvalidDrop : function(e, id){
38436
38437     }
38438 });/*
38439  * Based on:
38440  * Ext JS Library 1.1.1
38441  * Copyright(c) 2006-2007, Ext JS, LLC.
38442  *
38443  * Originally Released Under LGPL - original licence link has changed is not relivant.
38444  *
38445  * Fork - LGPL
38446  * <script type="text/javascript">
38447  */
38448  
38449
38450 /**
38451  * @class Roo.grid.ColumnModel
38452  * @extends Roo.util.Observable
38453  * This is the default implementation of a ColumnModel used by the Grid. It defines
38454  * the columns in the grid.
38455  * <br>Usage:<br>
38456  <pre><code>
38457  var colModel = new Roo.grid.ColumnModel([
38458         {header: "Ticker", width: 60, sortable: true, locked: true},
38459         {header: "Company Name", width: 150, sortable: true},
38460         {header: "Market Cap.", width: 100, sortable: true},
38461         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38462         {header: "Employees", width: 100, sortable: true, resizable: false}
38463  ]);
38464  </code></pre>
38465  * <p>
38466  
38467  * The config options listed for this class are options which may appear in each
38468  * individual column definition.
38469  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38470  * @constructor
38471  * @param {Object} config An Array of column config objects. See this class's
38472  * config objects for details.
38473 */
38474 Roo.grid.ColumnModel = function(config){
38475         /**
38476      * The config passed into the constructor
38477      */
38478     this.config = config;
38479     this.lookup = {};
38480
38481     // if no id, create one
38482     // if the column does not have a dataIndex mapping,
38483     // map it to the order it is in the config
38484     for(var i = 0, len = config.length; i < len; i++){
38485         var c = config[i];
38486         if(typeof c.dataIndex == "undefined"){
38487             c.dataIndex = i;
38488         }
38489         if(typeof c.renderer == "string"){
38490             c.renderer = Roo.util.Format[c.renderer];
38491         }
38492         if(typeof c.id == "undefined"){
38493             c.id = Roo.id();
38494         }
38495         if(c.editor && c.editor.xtype){
38496             c.editor  = Roo.factory(c.editor, Roo.grid);
38497         }
38498         if(c.editor && c.editor.isFormField){
38499             c.editor = new Roo.grid.GridEditor(c.editor);
38500         }
38501         this.lookup[c.id] = c;
38502     }
38503
38504     /**
38505      * The width of columns which have no width specified (defaults to 100)
38506      * @type Number
38507      */
38508     this.defaultWidth = 100;
38509
38510     /**
38511      * Default sortable of columns which have no sortable specified (defaults to false)
38512      * @type Boolean
38513      */
38514     this.defaultSortable = false;
38515
38516     this.addEvents({
38517         /**
38518              * @event widthchange
38519              * Fires when the width of a column changes.
38520              * @param {ColumnModel} this
38521              * @param {Number} columnIndex The column index
38522              * @param {Number} newWidth The new width
38523              */
38524             "widthchange": true,
38525         /**
38526              * @event headerchange
38527              * Fires when the text of a header changes.
38528              * @param {ColumnModel} this
38529              * @param {Number} columnIndex The column index
38530              * @param {Number} newText The new header text
38531              */
38532             "headerchange": true,
38533         /**
38534              * @event hiddenchange
38535              * Fires when a column is hidden or "unhidden".
38536              * @param {ColumnModel} this
38537              * @param {Number} columnIndex The column index
38538              * @param {Boolean} hidden true if hidden, false otherwise
38539              */
38540             "hiddenchange": true,
38541             /**
38542          * @event columnmoved
38543          * Fires when a column is moved.
38544          * @param {ColumnModel} this
38545          * @param {Number} oldIndex
38546          * @param {Number} newIndex
38547          */
38548         "columnmoved" : true,
38549         /**
38550          * @event columlockchange
38551          * Fires when a column's locked state is changed
38552          * @param {ColumnModel} this
38553          * @param {Number} colIndex
38554          * @param {Boolean} locked true if locked
38555          */
38556         "columnlockchange" : true
38557     });
38558     Roo.grid.ColumnModel.superclass.constructor.call(this);
38559 };
38560 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38561     /**
38562      * @cfg {String} header The header text to display in the Grid view.
38563      */
38564     /**
38565      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38566      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38567      * specified, the column's index is used as an index into the Record's data Array.
38568      */
38569     /**
38570      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38571      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38572      */
38573     /**
38574      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38575      * Defaults to the value of the {@link #defaultSortable} property.
38576      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38577      */
38578     /**
38579      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38580      */
38581     /**
38582      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38583      */
38584     /**
38585      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38586      */
38587     /**
38588      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38589      */
38590     /**
38591      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38592      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38593      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38594      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38595      */
38596        /**
38597      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38598      */
38599     /**
38600      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38601      */
38602     /**
38603      * @cfg {String} cursor (Optional)
38604      */
38605     /**
38606      * Returns the id of the column at the specified index.
38607      * @param {Number} index The column index
38608      * @return {String} the id
38609      */
38610     getColumnId : function(index){
38611         return this.config[index].id;
38612     },
38613
38614     /**
38615      * Returns the column for a specified id.
38616      * @param {String} id The column id
38617      * @return {Object} the column
38618      */
38619     getColumnById : function(id){
38620         return this.lookup[id];
38621     },
38622
38623     
38624     /**
38625      * Returns the column for a specified dataIndex.
38626      * @param {String} dataIndex The column dataIndex
38627      * @return {Object|Boolean} the column or false if not found
38628      */
38629     getColumnByDataIndex: function(dataIndex){
38630         var index = this.findColumnIndex(dataIndex);
38631         return index > -1 ? this.config[index] : false;
38632     },
38633     
38634     /**
38635      * Returns the index for a specified column id.
38636      * @param {String} id The column id
38637      * @return {Number} the index, or -1 if not found
38638      */
38639     getIndexById : function(id){
38640         for(var i = 0, len = this.config.length; i < len; i++){
38641             if(this.config[i].id == id){
38642                 return i;
38643             }
38644         }
38645         return -1;
38646     },
38647     
38648     /**
38649      * Returns the index for a specified column dataIndex.
38650      * @param {String} dataIndex The column dataIndex
38651      * @return {Number} the index, or -1 if not found
38652      */
38653     
38654     findColumnIndex : function(dataIndex){
38655         for(var i = 0, len = this.config.length; i < len; i++){
38656             if(this.config[i].dataIndex == dataIndex){
38657                 return i;
38658             }
38659         }
38660         return -1;
38661     },
38662     
38663     
38664     moveColumn : function(oldIndex, newIndex){
38665         var c = this.config[oldIndex];
38666         this.config.splice(oldIndex, 1);
38667         this.config.splice(newIndex, 0, c);
38668         this.dataMap = null;
38669         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38670     },
38671
38672     isLocked : function(colIndex){
38673         return this.config[colIndex].locked === true;
38674     },
38675
38676     setLocked : function(colIndex, value, suppressEvent){
38677         if(this.isLocked(colIndex) == value){
38678             return;
38679         }
38680         this.config[colIndex].locked = value;
38681         if(!suppressEvent){
38682             this.fireEvent("columnlockchange", this, colIndex, value);
38683         }
38684     },
38685
38686     getTotalLockedWidth : function(){
38687         var totalWidth = 0;
38688         for(var i = 0; i < this.config.length; i++){
38689             if(this.isLocked(i) && !this.isHidden(i)){
38690                 this.totalWidth += this.getColumnWidth(i);
38691             }
38692         }
38693         return totalWidth;
38694     },
38695
38696     getLockedCount : function(){
38697         for(var i = 0, len = this.config.length; i < len; i++){
38698             if(!this.isLocked(i)){
38699                 return i;
38700             }
38701         }
38702     },
38703
38704     /**
38705      * Returns the number of columns.
38706      * @return {Number}
38707      */
38708     getColumnCount : function(visibleOnly){
38709         if(visibleOnly === true){
38710             var c = 0;
38711             for(var i = 0, len = this.config.length; i < len; i++){
38712                 if(!this.isHidden(i)){
38713                     c++;
38714                 }
38715             }
38716             return c;
38717         }
38718         return this.config.length;
38719     },
38720
38721     /**
38722      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38723      * @param {Function} fn
38724      * @param {Object} scope (optional)
38725      * @return {Array} result
38726      */
38727     getColumnsBy : function(fn, scope){
38728         var r = [];
38729         for(var i = 0, len = this.config.length; i < len; i++){
38730             var c = this.config[i];
38731             if(fn.call(scope||this, c, i) === true){
38732                 r[r.length] = c;
38733             }
38734         }
38735         return r;
38736     },
38737
38738     /**
38739      * Returns true if the specified column is sortable.
38740      * @param {Number} col The column index
38741      * @return {Boolean}
38742      */
38743     isSortable : function(col){
38744         if(typeof this.config[col].sortable == "undefined"){
38745             return this.defaultSortable;
38746         }
38747         return this.config[col].sortable;
38748     },
38749
38750     /**
38751      * Returns the rendering (formatting) function defined for the column.
38752      * @param {Number} col The column index.
38753      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38754      */
38755     getRenderer : function(col){
38756         if(!this.config[col].renderer){
38757             return Roo.grid.ColumnModel.defaultRenderer;
38758         }
38759         return this.config[col].renderer;
38760     },
38761
38762     /**
38763      * Sets the rendering (formatting) function for a column.
38764      * @param {Number} col The column index
38765      * @param {Function} fn The function to use to process the cell's raw data
38766      * to return HTML markup for the grid view. The render function is called with
38767      * the following parameters:<ul>
38768      * <li>Data value.</li>
38769      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38770      * <li>css A CSS style string to apply to the table cell.</li>
38771      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38772      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38773      * <li>Row index</li>
38774      * <li>Column index</li>
38775      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38776      */
38777     setRenderer : function(col, fn){
38778         this.config[col].renderer = fn;
38779     },
38780
38781     /**
38782      * Returns the width for the specified column.
38783      * @param {Number} col The column index
38784      * @return {Number}
38785      */
38786     getColumnWidth : function(col){
38787         return this.config[col].width * 1 || this.defaultWidth;
38788     },
38789
38790     /**
38791      * Sets the width for a column.
38792      * @param {Number} col The column index
38793      * @param {Number} width The new width
38794      */
38795     setColumnWidth : function(col, width, suppressEvent){
38796         this.config[col].width = width;
38797         this.totalWidth = null;
38798         if(!suppressEvent){
38799              this.fireEvent("widthchange", this, col, width);
38800         }
38801     },
38802
38803     /**
38804      * Returns the total width of all columns.
38805      * @param {Boolean} includeHidden True to include hidden column widths
38806      * @return {Number}
38807      */
38808     getTotalWidth : function(includeHidden){
38809         if(!this.totalWidth){
38810             this.totalWidth = 0;
38811             for(var i = 0, len = this.config.length; i < len; i++){
38812                 if(includeHidden || !this.isHidden(i)){
38813                     this.totalWidth += this.getColumnWidth(i);
38814                 }
38815             }
38816         }
38817         return this.totalWidth;
38818     },
38819
38820     /**
38821      * Returns the header for the specified column.
38822      * @param {Number} col The column index
38823      * @return {String}
38824      */
38825     getColumnHeader : function(col){
38826         return this.config[col].header;
38827     },
38828
38829     /**
38830      * Sets the header for a column.
38831      * @param {Number} col The column index
38832      * @param {String} header The new header
38833      */
38834     setColumnHeader : function(col, header){
38835         this.config[col].header = header;
38836         this.fireEvent("headerchange", this, col, header);
38837     },
38838
38839     /**
38840      * Returns the tooltip for the specified column.
38841      * @param {Number} col The column index
38842      * @return {String}
38843      */
38844     getColumnTooltip : function(col){
38845             return this.config[col].tooltip;
38846     },
38847     /**
38848      * Sets the tooltip for a column.
38849      * @param {Number} col The column index
38850      * @param {String} tooltip The new tooltip
38851      */
38852     setColumnTooltip : function(col, tooltip){
38853             this.config[col].tooltip = tooltip;
38854     },
38855
38856     /**
38857      * Returns the dataIndex for the specified column.
38858      * @param {Number} col The column index
38859      * @return {Number}
38860      */
38861     getDataIndex : function(col){
38862         return this.config[col].dataIndex;
38863     },
38864
38865     /**
38866      * Sets the dataIndex for a column.
38867      * @param {Number} col The column index
38868      * @param {Number} dataIndex The new dataIndex
38869      */
38870     setDataIndex : function(col, dataIndex){
38871         this.config[col].dataIndex = dataIndex;
38872     },
38873
38874     
38875     
38876     /**
38877      * Returns true if the cell is editable.
38878      * @param {Number} colIndex The column index
38879      * @param {Number} rowIndex The row index
38880      * @return {Boolean}
38881      */
38882     isCellEditable : function(colIndex, rowIndex){
38883         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38884     },
38885
38886     /**
38887      * Returns the editor defined for the cell/column.
38888      * return false or null to disable editing.
38889      * @param {Number} colIndex The column index
38890      * @param {Number} rowIndex The row index
38891      * @return {Object}
38892      */
38893     getCellEditor : function(colIndex, rowIndex){
38894         return this.config[colIndex].editor;
38895     },
38896
38897     /**
38898      * Sets if a column is editable.
38899      * @param {Number} col The column index
38900      * @param {Boolean} editable True if the column is editable
38901      */
38902     setEditable : function(col, editable){
38903         this.config[col].editable = editable;
38904     },
38905
38906
38907     /**
38908      * Returns true if the column is hidden.
38909      * @param {Number} colIndex The column index
38910      * @return {Boolean}
38911      */
38912     isHidden : function(colIndex){
38913         return this.config[colIndex].hidden;
38914     },
38915
38916
38917     /**
38918      * Returns true if the column width cannot be changed
38919      */
38920     isFixed : function(colIndex){
38921         return this.config[colIndex].fixed;
38922     },
38923
38924     /**
38925      * Returns true if the column can be resized
38926      * @return {Boolean}
38927      */
38928     isResizable : function(colIndex){
38929         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38930     },
38931     /**
38932      * Sets if a column is hidden.
38933      * @param {Number} colIndex The column index
38934      * @param {Boolean} hidden True if the column is hidden
38935      */
38936     setHidden : function(colIndex, hidden){
38937         this.config[colIndex].hidden = hidden;
38938         this.totalWidth = null;
38939         this.fireEvent("hiddenchange", this, colIndex, hidden);
38940     },
38941
38942     /**
38943      * Sets the editor for a column.
38944      * @param {Number} col The column index
38945      * @param {Object} editor The editor object
38946      */
38947     setEditor : function(col, editor){
38948         this.config[col].editor = editor;
38949     }
38950 });
38951
38952 Roo.grid.ColumnModel.defaultRenderer = function(value){
38953         if(typeof value == "string" && value.length < 1){
38954             return "&#160;";
38955         }
38956         return value;
38957 };
38958
38959 // Alias for backwards compatibility
38960 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38961 /*
38962  * Based on:
38963  * Ext JS Library 1.1.1
38964  * Copyright(c) 2006-2007, Ext JS, LLC.
38965  *
38966  * Originally Released Under LGPL - original licence link has changed is not relivant.
38967  *
38968  * Fork - LGPL
38969  * <script type="text/javascript">
38970  */
38971
38972 /**
38973  * @class Roo.grid.AbstractSelectionModel
38974  * @extends Roo.util.Observable
38975  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38976  * implemented by descendant classes.  This class should not be directly instantiated.
38977  * @constructor
38978  */
38979 Roo.grid.AbstractSelectionModel = function(){
38980     this.locked = false;
38981     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38982 };
38983
38984 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38985     /** @ignore Called by the grid automatically. Do not call directly. */
38986     init : function(grid){
38987         this.grid = grid;
38988         this.initEvents();
38989     },
38990
38991     /**
38992      * Locks the selections.
38993      */
38994     lock : function(){
38995         this.locked = true;
38996     },
38997
38998     /**
38999      * Unlocks the selections.
39000      */
39001     unlock : function(){
39002         this.locked = false;
39003     },
39004
39005     /**
39006      * Returns true if the selections are locked.
39007      * @return {Boolean}
39008      */
39009     isLocked : function(){
39010         return this.locked;
39011     }
39012 });/*
39013  * Based on:
39014  * Ext JS Library 1.1.1
39015  * Copyright(c) 2006-2007, Ext JS, LLC.
39016  *
39017  * Originally Released Under LGPL - original licence link has changed is not relivant.
39018  *
39019  * Fork - LGPL
39020  * <script type="text/javascript">
39021  */
39022 /**
39023  * @extends Roo.grid.AbstractSelectionModel
39024  * @class Roo.grid.RowSelectionModel
39025  * The default SelectionModel used by {@link Roo.grid.Grid}.
39026  * It supports multiple selections and keyboard selection/navigation. 
39027  * @constructor
39028  * @param {Object} config
39029  */
39030 Roo.grid.RowSelectionModel = function(config){
39031     Roo.apply(this, config);
39032     this.selections = new Roo.util.MixedCollection(false, function(o){
39033         return o.id;
39034     });
39035
39036     this.last = false;
39037     this.lastActive = false;
39038
39039     this.addEvents({
39040         /**
39041              * @event selectionchange
39042              * Fires when the selection changes
39043              * @param {SelectionModel} this
39044              */
39045             "selectionchange" : true,
39046         /**
39047              * @event afterselectionchange
39048              * Fires after the selection changes (eg. by key press or clicking)
39049              * @param {SelectionModel} this
39050              */
39051             "afterselectionchange" : true,
39052         /**
39053              * @event beforerowselect
39054              * Fires when a row is selected being selected, return false to cancel.
39055              * @param {SelectionModel} this
39056              * @param {Number} rowIndex The selected index
39057              * @param {Boolean} keepExisting False if other selections will be cleared
39058              */
39059             "beforerowselect" : true,
39060         /**
39061              * @event rowselect
39062              * Fires when a row is selected.
39063              * @param {SelectionModel} this
39064              * @param {Number} rowIndex The selected index
39065              * @param {Roo.data.Record} r The record
39066              */
39067             "rowselect" : true,
39068         /**
39069              * @event rowdeselect
39070              * Fires when a row is deselected.
39071              * @param {SelectionModel} this
39072              * @param {Number} rowIndex The selected index
39073              */
39074         "rowdeselect" : true
39075     });
39076     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39077     this.locked = false;
39078 };
39079
39080 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39081     /**
39082      * @cfg {Boolean} singleSelect
39083      * True to allow selection of only one row at a time (defaults to false)
39084      */
39085     singleSelect : false,
39086
39087     // private
39088     initEvents : function(){
39089
39090         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39091             this.grid.on("mousedown", this.handleMouseDown, this);
39092         }else{ // allow click to work like normal
39093             this.grid.on("rowclick", this.handleDragableRowClick, this);
39094         }
39095
39096         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39097             "up" : function(e){
39098                 if(!e.shiftKey){
39099                     this.selectPrevious(e.shiftKey);
39100                 }else if(this.last !== false && this.lastActive !== false){
39101                     var last = this.last;
39102                     this.selectRange(this.last,  this.lastActive-1);
39103                     this.grid.getView().focusRow(this.lastActive);
39104                     if(last !== false){
39105                         this.last = last;
39106                     }
39107                 }else{
39108                     this.selectFirstRow();
39109                 }
39110                 this.fireEvent("afterselectionchange", this);
39111             },
39112             "down" : function(e){
39113                 if(!e.shiftKey){
39114                     this.selectNext(e.shiftKey);
39115                 }else if(this.last !== false && this.lastActive !== false){
39116                     var last = this.last;
39117                     this.selectRange(this.last,  this.lastActive+1);
39118                     this.grid.getView().focusRow(this.lastActive);
39119                     if(last !== false){
39120                         this.last = last;
39121                     }
39122                 }else{
39123                     this.selectFirstRow();
39124                 }
39125                 this.fireEvent("afterselectionchange", this);
39126             },
39127             scope: this
39128         });
39129
39130         var view = this.grid.view;
39131         view.on("refresh", this.onRefresh, this);
39132         view.on("rowupdated", this.onRowUpdated, this);
39133         view.on("rowremoved", this.onRemove, this);
39134     },
39135
39136     // private
39137     onRefresh : function(){
39138         var ds = this.grid.dataSource, i, v = this.grid.view;
39139         var s = this.selections;
39140         s.each(function(r){
39141             if((i = ds.indexOfId(r.id)) != -1){
39142                 v.onRowSelect(i);
39143             }else{
39144                 s.remove(r);
39145             }
39146         });
39147     },
39148
39149     // private
39150     onRemove : function(v, index, r){
39151         this.selections.remove(r);
39152     },
39153
39154     // private
39155     onRowUpdated : function(v, index, r){
39156         if(this.isSelected(r)){
39157             v.onRowSelect(index);
39158         }
39159     },
39160
39161     /**
39162      * Select records.
39163      * @param {Array} records The records to select
39164      * @param {Boolean} keepExisting (optional) True to keep existing selections
39165      */
39166     selectRecords : function(records, keepExisting){
39167         if(!keepExisting){
39168             this.clearSelections();
39169         }
39170         var ds = this.grid.dataSource;
39171         for(var i = 0, len = records.length; i < len; i++){
39172             this.selectRow(ds.indexOf(records[i]), true);
39173         }
39174     },
39175
39176     /**
39177      * Gets the number of selected rows.
39178      * @return {Number}
39179      */
39180     getCount : function(){
39181         return this.selections.length;
39182     },
39183
39184     /**
39185      * Selects the first row in the grid.
39186      */
39187     selectFirstRow : function(){
39188         this.selectRow(0);
39189     },
39190
39191     /**
39192      * Select the last row.
39193      * @param {Boolean} keepExisting (optional) True to keep existing selections
39194      */
39195     selectLastRow : function(keepExisting){
39196         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39197     },
39198
39199     /**
39200      * Selects the row immediately following the last selected row.
39201      * @param {Boolean} keepExisting (optional) True to keep existing selections
39202      */
39203     selectNext : function(keepExisting){
39204         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39205             this.selectRow(this.last+1, keepExisting);
39206             this.grid.getView().focusRow(this.last);
39207         }
39208     },
39209
39210     /**
39211      * Selects the row that precedes the last selected row.
39212      * @param {Boolean} keepExisting (optional) True to keep existing selections
39213      */
39214     selectPrevious : function(keepExisting){
39215         if(this.last){
39216             this.selectRow(this.last-1, keepExisting);
39217             this.grid.getView().focusRow(this.last);
39218         }
39219     },
39220
39221     /**
39222      * Returns the selected records
39223      * @return {Array} Array of selected records
39224      */
39225     getSelections : function(){
39226         return [].concat(this.selections.items);
39227     },
39228
39229     /**
39230      * Returns the first selected record.
39231      * @return {Record}
39232      */
39233     getSelected : function(){
39234         return this.selections.itemAt(0);
39235     },
39236
39237
39238     /**
39239      * Clears all selections.
39240      */
39241     clearSelections : function(fast){
39242         if(this.locked) return;
39243         if(fast !== true){
39244             var ds = this.grid.dataSource;
39245             var s = this.selections;
39246             s.each(function(r){
39247                 this.deselectRow(ds.indexOfId(r.id));
39248             }, this);
39249             s.clear();
39250         }else{
39251             this.selections.clear();
39252         }
39253         this.last = false;
39254     },
39255
39256
39257     /**
39258      * Selects all rows.
39259      */
39260     selectAll : function(){
39261         if(this.locked) return;
39262         this.selections.clear();
39263         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39264             this.selectRow(i, true);
39265         }
39266     },
39267
39268     /**
39269      * Returns True if there is a selection.
39270      * @return {Boolean}
39271      */
39272     hasSelection : function(){
39273         return this.selections.length > 0;
39274     },
39275
39276     /**
39277      * Returns True if the specified row is selected.
39278      * @param {Number/Record} record The record or index of the record to check
39279      * @return {Boolean}
39280      */
39281     isSelected : function(index){
39282         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39283         return (r && this.selections.key(r.id) ? true : false);
39284     },
39285
39286     /**
39287      * Returns True if the specified record id is selected.
39288      * @param {String} id The id of record to check
39289      * @return {Boolean}
39290      */
39291     isIdSelected : function(id){
39292         return (this.selections.key(id) ? true : false);
39293     },
39294
39295     // private
39296     handleMouseDown : function(e, t){
39297         var view = this.grid.getView(), rowIndex;
39298         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39299             return;
39300         };
39301         if(e.shiftKey && this.last !== false){
39302             var last = this.last;
39303             this.selectRange(last, rowIndex, e.ctrlKey);
39304             this.last = last; // reset the last
39305             view.focusRow(rowIndex);
39306         }else{
39307             var isSelected = this.isSelected(rowIndex);
39308             if(e.button !== 0 && isSelected){
39309                 view.focusRow(rowIndex);
39310             }else if(e.ctrlKey && isSelected){
39311                 this.deselectRow(rowIndex);
39312             }else if(!isSelected){
39313                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39314                 view.focusRow(rowIndex);
39315             }
39316         }
39317         this.fireEvent("afterselectionchange", this);
39318     },
39319     // private
39320     handleDragableRowClick :  function(grid, rowIndex, e) 
39321     {
39322         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39323             this.selectRow(rowIndex, false);
39324             grid.view.focusRow(rowIndex);
39325              this.fireEvent("afterselectionchange", this);
39326         }
39327     },
39328     
39329     /**
39330      * Selects multiple rows.
39331      * @param {Array} rows Array of the indexes of the row to select
39332      * @param {Boolean} keepExisting (optional) True to keep existing selections
39333      */
39334     selectRows : function(rows, keepExisting){
39335         if(!keepExisting){
39336             this.clearSelections();
39337         }
39338         for(var i = 0, len = rows.length; i < len; i++){
39339             this.selectRow(rows[i], true);
39340         }
39341     },
39342
39343     /**
39344      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39345      * @param {Number} startRow The index of the first row in the range
39346      * @param {Number} endRow The index of the last row in the range
39347      * @param {Boolean} keepExisting (optional) True to retain existing selections
39348      */
39349     selectRange : function(startRow, endRow, keepExisting){
39350         if(this.locked) return;
39351         if(!keepExisting){
39352             this.clearSelections();
39353         }
39354         if(startRow <= endRow){
39355             for(var i = startRow; i <= endRow; i++){
39356                 this.selectRow(i, true);
39357             }
39358         }else{
39359             for(var i = startRow; i >= endRow; i--){
39360                 this.selectRow(i, true);
39361             }
39362         }
39363     },
39364
39365     /**
39366      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39367      * @param {Number} startRow The index of the first row in the range
39368      * @param {Number} endRow The index of the last row in the range
39369      */
39370     deselectRange : function(startRow, endRow, preventViewNotify){
39371         if(this.locked) return;
39372         for(var i = startRow; i <= endRow; i++){
39373             this.deselectRow(i, preventViewNotify);
39374         }
39375     },
39376
39377     /**
39378      * Selects a row.
39379      * @param {Number} row The index of the row to select
39380      * @param {Boolean} keepExisting (optional) True to keep existing selections
39381      */
39382     selectRow : function(index, keepExisting, preventViewNotify){
39383         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39384         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39385             if(!keepExisting || this.singleSelect){
39386                 this.clearSelections();
39387             }
39388             var r = this.grid.dataSource.getAt(index);
39389             this.selections.add(r);
39390             this.last = this.lastActive = index;
39391             if(!preventViewNotify){
39392                 this.grid.getView().onRowSelect(index);
39393             }
39394             this.fireEvent("rowselect", this, index, r);
39395             this.fireEvent("selectionchange", this);
39396         }
39397     },
39398
39399     /**
39400      * Deselects a row.
39401      * @param {Number} row The index of the row to deselect
39402      */
39403     deselectRow : function(index, preventViewNotify){
39404         if(this.locked) return;
39405         if(this.last == index){
39406             this.last = false;
39407         }
39408         if(this.lastActive == index){
39409             this.lastActive = false;
39410         }
39411         var r = this.grid.dataSource.getAt(index);
39412         this.selections.remove(r);
39413         if(!preventViewNotify){
39414             this.grid.getView().onRowDeselect(index);
39415         }
39416         this.fireEvent("rowdeselect", this, index);
39417         this.fireEvent("selectionchange", this);
39418     },
39419
39420     // private
39421     restoreLast : function(){
39422         if(this._last){
39423             this.last = this._last;
39424         }
39425     },
39426
39427     // private
39428     acceptsNav : function(row, col, cm){
39429         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39430     },
39431
39432     // private
39433     onEditorKey : function(field, e){
39434         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39435         if(k == e.TAB){
39436             e.stopEvent();
39437             ed.completeEdit();
39438             if(e.shiftKey){
39439                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39440             }else{
39441                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39442             }
39443         }else if(k == e.ENTER && !e.ctrlKey){
39444             e.stopEvent();
39445             ed.completeEdit();
39446             if(e.shiftKey){
39447                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39448             }else{
39449                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39450             }
39451         }else if(k == e.ESC){
39452             ed.cancelEdit();
39453         }
39454         if(newCell){
39455             g.startEditing(newCell[0], newCell[1]);
39456         }
39457     }
39458 });/*
39459  * Based on:
39460  * Ext JS Library 1.1.1
39461  * Copyright(c) 2006-2007, Ext JS, LLC.
39462  *
39463  * Originally Released Under LGPL - original licence link has changed is not relivant.
39464  *
39465  * Fork - LGPL
39466  * <script type="text/javascript">
39467  */
39468 /**
39469  * @class Roo.grid.CellSelectionModel
39470  * @extends Roo.grid.AbstractSelectionModel
39471  * This class provides the basic implementation for cell selection in a grid.
39472  * @constructor
39473  * @param {Object} config The object containing the configuration of this model.
39474  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39475  */
39476 Roo.grid.CellSelectionModel = function(config){
39477     Roo.apply(this, config);
39478
39479     this.selection = null;
39480
39481     this.addEvents({
39482         /**
39483              * @event beforerowselect
39484              * Fires before a cell is selected.
39485              * @param {SelectionModel} this
39486              * @param {Number} rowIndex The selected row index
39487              * @param {Number} colIndex The selected cell index
39488              */
39489             "beforecellselect" : true,
39490         /**
39491              * @event cellselect
39492              * Fires when a cell is selected.
39493              * @param {SelectionModel} this
39494              * @param {Number} rowIndex The selected row index
39495              * @param {Number} colIndex The selected cell index
39496              */
39497             "cellselect" : true,
39498         /**
39499              * @event selectionchange
39500              * Fires when the active selection changes.
39501              * @param {SelectionModel} this
39502              * @param {Object} selection null for no selection or an object (o) with two properties
39503                 <ul>
39504                 <li>o.record: the record object for the row the selection is in</li>
39505                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39506                 </ul>
39507              */
39508             "selectionchange" : true,
39509         /**
39510              * @event tabend
39511              * Fires when the tab (or enter) was pressed on the last editable cell
39512              * You can use this to trigger add new row.
39513              * @param {SelectionModel} this
39514              */
39515             "tabend" : true,
39516          /**
39517              * @event beforeeditnext
39518              * Fires before the next editable sell is made active
39519              * You can use this to skip to another cell or fire the tabend
39520              *    if you set cell to false
39521              * @param {Object} eventdata object : { cell : [ row, col ] } 
39522              */
39523             "beforeeditnext" : true
39524     });
39525     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39526 };
39527
39528 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39529     
39530     enter_is_tab: false,
39531
39532     /** @ignore */
39533     initEvents : function(){
39534         this.grid.on("mousedown", this.handleMouseDown, this);
39535         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39536         var view = this.grid.view;
39537         view.on("refresh", this.onViewChange, this);
39538         view.on("rowupdated", this.onRowUpdated, this);
39539         view.on("beforerowremoved", this.clearSelections, this);
39540         view.on("beforerowsinserted", this.clearSelections, this);
39541         if(this.grid.isEditor){
39542             this.grid.on("beforeedit", this.beforeEdit,  this);
39543         }
39544     },
39545
39546         //private
39547     beforeEdit : function(e){
39548         this.select(e.row, e.column, false, true, e.record);
39549     },
39550
39551         //private
39552     onRowUpdated : function(v, index, r){
39553         if(this.selection && this.selection.record == r){
39554             v.onCellSelect(index, this.selection.cell[1]);
39555         }
39556     },
39557
39558         //private
39559     onViewChange : function(){
39560         this.clearSelections(true);
39561     },
39562
39563         /**
39564          * Returns the currently selected cell,.
39565          * @return {Array} The selected cell (row, column) or null if none selected.
39566          */
39567     getSelectedCell : function(){
39568         return this.selection ? this.selection.cell : null;
39569     },
39570
39571     /**
39572      * Clears all selections.
39573      * @param {Boolean} true to prevent the gridview from being notified about the change.
39574      */
39575     clearSelections : function(preventNotify){
39576         var s = this.selection;
39577         if(s){
39578             if(preventNotify !== true){
39579                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39580             }
39581             this.selection = null;
39582             this.fireEvent("selectionchange", this, null);
39583         }
39584     },
39585
39586     /**
39587      * Returns true if there is a selection.
39588      * @return {Boolean}
39589      */
39590     hasSelection : function(){
39591         return this.selection ? true : false;
39592     },
39593
39594     /** @ignore */
39595     handleMouseDown : function(e, t){
39596         var v = this.grid.getView();
39597         if(this.isLocked()){
39598             return;
39599         };
39600         var row = v.findRowIndex(t);
39601         var cell = v.findCellIndex(t);
39602         if(row !== false && cell !== false){
39603             this.select(row, cell);
39604         }
39605     },
39606
39607     /**
39608      * Selects a cell.
39609      * @param {Number} rowIndex
39610      * @param {Number} collIndex
39611      */
39612     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39613         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39614             this.clearSelections();
39615             r = r || this.grid.dataSource.getAt(rowIndex);
39616             this.selection = {
39617                 record : r,
39618                 cell : [rowIndex, colIndex]
39619             };
39620             if(!preventViewNotify){
39621                 var v = this.grid.getView();
39622                 v.onCellSelect(rowIndex, colIndex);
39623                 if(preventFocus !== true){
39624                     v.focusCell(rowIndex, colIndex);
39625                 }
39626             }
39627             this.fireEvent("cellselect", this, rowIndex, colIndex);
39628             this.fireEvent("selectionchange", this, this.selection);
39629         }
39630     },
39631
39632         //private
39633     isSelectable : function(rowIndex, colIndex, cm){
39634         return !cm.isHidden(colIndex);
39635     },
39636
39637     /** @ignore */
39638     handleKeyDown : function(e){
39639         //Roo.log('Cell Sel Model handleKeyDown');
39640         if(!e.isNavKeyPress()){
39641             return;
39642         }
39643         var g = this.grid, s = this.selection;
39644         if(!s){
39645             e.stopEvent();
39646             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39647             if(cell){
39648                 this.select(cell[0], cell[1]);
39649             }
39650             return;
39651         }
39652         var sm = this;
39653         var walk = function(row, col, step){
39654             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39655         };
39656         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39657         var newCell;
39658
39659       
39660
39661         switch(k){
39662             case e.TAB:
39663                 // handled by onEditorKey
39664                 if (g.isEditor && g.editing) {
39665                     return;
39666                 }
39667                 if(e.shiftKey) {
39668                     newCell = walk(r, c-1, -1);
39669                 } else {
39670                     newCell = walk(r, c+1, 1);
39671                 }
39672                 break;
39673             
39674             case e.DOWN:
39675                newCell = walk(r+1, c, 1);
39676                 break;
39677             
39678             case e.UP:
39679                 newCell = walk(r-1, c, -1);
39680                 break;
39681             
39682             case e.RIGHT:
39683                 newCell = walk(r, c+1, 1);
39684                 break;
39685             
39686             case e.LEFT:
39687                 newCell = walk(r, c-1, -1);
39688                 break;
39689             
39690             case e.ENTER:
39691                 
39692                 if(g.isEditor && !g.editing){
39693                    g.startEditing(r, c);
39694                    e.stopEvent();
39695                    return;
39696                 }
39697                 
39698                 
39699              break;
39700         };
39701         if(newCell){
39702             this.select(newCell[0], newCell[1]);
39703             e.stopEvent();
39704             
39705         }
39706     },
39707
39708     acceptsNav : function(row, col, cm){
39709         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39710     },
39711     /**
39712      * Selects a cell.
39713      * @param {Number} field (not used) - as it's normally used as a listener
39714      * @param {Number} e - event - fake it by using
39715      *
39716      * var e = Roo.EventObjectImpl.prototype;
39717      * e.keyCode = e.TAB
39718      *
39719      * 
39720      */
39721     onEditorKey : function(field, e){
39722         
39723         var k = e.getKey(),
39724             newCell,
39725             g = this.grid,
39726             ed = g.activeEditor,
39727             forward = false;
39728         ///Roo.log('onEditorKey' + k);
39729         
39730         
39731         if (this.enter_is_tab && k == e.ENTER) {
39732             k = e.TAB;
39733         }
39734         
39735         if(k == e.TAB){
39736             if(e.shiftKey){
39737                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39738             }else{
39739                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39740                 forward = true;
39741             }
39742             
39743             e.stopEvent();
39744             
39745         } else if(k == e.ENTER &&  !e.ctrlKey){
39746             ed.completeEdit();
39747             e.stopEvent();
39748             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39749         
39750                 } else if(k == e.ESC){
39751             ed.cancelEdit();
39752         }
39753                 
39754         if (newCell) {
39755             var ecall = { cell : newCell, forward : forward };
39756             this.fireEvent('beforeeditnext', ecall );
39757             newCell = ecall.cell;
39758                         forward = ecall.forward;
39759         }
39760                 
39761         if(newCell){
39762             //Roo.log('next cell after edit');
39763             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39764         } else if (forward) {
39765             // tabbed past last
39766             this.fireEvent.defer(100, this, ['tabend',this]);
39767         }
39768     }
39769 });/*
39770  * Based on:
39771  * Ext JS Library 1.1.1
39772  * Copyright(c) 2006-2007, Ext JS, LLC.
39773  *
39774  * Originally Released Under LGPL - original licence link has changed is not relivant.
39775  *
39776  * Fork - LGPL
39777  * <script type="text/javascript">
39778  */
39779  
39780 /**
39781  * @class Roo.grid.EditorGrid
39782  * @extends Roo.grid.Grid
39783  * Class for creating and editable grid.
39784  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39785  * The container MUST have some type of size defined for the grid to fill. The container will be 
39786  * automatically set to position relative if it isn't already.
39787  * @param {Object} dataSource The data model to bind to
39788  * @param {Object} colModel The column model with info about this grid's columns
39789  */
39790 Roo.grid.EditorGrid = function(container, config){
39791     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39792     this.getGridEl().addClass("xedit-grid");
39793
39794     if(!this.selModel){
39795         this.selModel = new Roo.grid.CellSelectionModel();
39796     }
39797
39798     this.activeEditor = null;
39799
39800         this.addEvents({
39801             /**
39802              * @event beforeedit
39803              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39804              * <ul style="padding:5px;padding-left:16px;">
39805              * <li>grid - This grid</li>
39806              * <li>record - The record being edited</li>
39807              * <li>field - The field name being edited</li>
39808              * <li>value - The value for the field being edited.</li>
39809              * <li>row - The grid row index</li>
39810              * <li>column - The grid column index</li>
39811              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39812              * </ul>
39813              * @param {Object} e An edit event (see above for description)
39814              */
39815             "beforeedit" : true,
39816             /**
39817              * @event afteredit
39818              * Fires after a cell is edited. <br />
39819              * <ul style="padding:5px;padding-left:16px;">
39820              * <li>grid - This grid</li>
39821              * <li>record - The record being edited</li>
39822              * <li>field - The field name being edited</li>
39823              * <li>value - The value being set</li>
39824              * <li>originalValue - The original value for the field, before the edit.</li>
39825              * <li>row - The grid row index</li>
39826              * <li>column - The grid column index</li>
39827              * </ul>
39828              * @param {Object} e An edit event (see above for description)
39829              */
39830             "afteredit" : true,
39831             /**
39832              * @event validateedit
39833              * Fires after a cell is edited, but before the value is set in the record. 
39834          * You can use this to modify the value being set in the field, Return false
39835              * to cancel the change. The edit event object has the following properties <br />
39836              * <ul style="padding:5px;padding-left:16px;">
39837          * <li>editor - This editor</li>
39838              * <li>grid - This grid</li>
39839              * <li>record - The record being edited</li>
39840              * <li>field - The field name being edited</li>
39841              * <li>value - The value being set</li>
39842              * <li>originalValue - The original value for the field, before the edit.</li>
39843              * <li>row - The grid row index</li>
39844              * <li>column - The grid column index</li>
39845              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39846              * </ul>
39847              * @param {Object} e An edit event (see above for description)
39848              */
39849             "validateedit" : true
39850         });
39851     this.on("bodyscroll", this.stopEditing,  this);
39852     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39853 };
39854
39855 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39856     /**
39857      * @cfg {Number} clicksToEdit
39858      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39859      */
39860     clicksToEdit: 2,
39861
39862     // private
39863     isEditor : true,
39864     // private
39865     trackMouseOver: false, // causes very odd FF errors
39866
39867     onCellDblClick : function(g, row, col){
39868         this.startEditing(row, col);
39869     },
39870
39871     onEditComplete : function(ed, value, startValue){
39872         this.editing = false;
39873         this.activeEditor = null;
39874         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39875         var r = ed.record;
39876         var field = this.colModel.getDataIndex(ed.col);
39877         var e = {
39878             grid: this,
39879             record: r,
39880             field: field,
39881             originalValue: startValue,
39882             value: value,
39883             row: ed.row,
39884             column: ed.col,
39885             cancel:false,
39886             editor: ed
39887         };
39888         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39889         cell.show();
39890           
39891         if(String(value) !== String(startValue)){
39892             
39893             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39894                 r.set(field, e.value);
39895                 // if we are dealing with a combo box..
39896                 // then we also set the 'name' colum to be the displayField
39897                 if (ed.field.displayField && ed.field.name) {
39898                     r.set(ed.field.name, ed.field.el.dom.value);
39899                 }
39900                 
39901                 delete e.cancel; //?? why!!!
39902                 this.fireEvent("afteredit", e);
39903             }
39904         } else {
39905             this.fireEvent("afteredit", e); // always fire it!
39906         }
39907         this.view.focusCell(ed.row, ed.col);
39908     },
39909
39910     /**
39911      * Starts editing the specified for the specified row/column
39912      * @param {Number} rowIndex
39913      * @param {Number} colIndex
39914      */
39915     startEditing : function(row, col){
39916         this.stopEditing();
39917         if(this.colModel.isCellEditable(col, row)){
39918             this.view.ensureVisible(row, col, true);
39919           
39920             var r = this.dataSource.getAt(row);
39921             var field = this.colModel.getDataIndex(col);
39922             var cell = Roo.get(this.view.getCell(row,col));
39923             var e = {
39924                 grid: this,
39925                 record: r,
39926                 field: field,
39927                 value: r.data[field],
39928                 row: row,
39929                 column: col,
39930                 cancel:false 
39931             };
39932             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39933                 this.editing = true;
39934                 var ed = this.colModel.getCellEditor(col, row);
39935                 
39936                 if (!ed) {
39937                     return;
39938                 }
39939                 if(!ed.rendered){
39940                     ed.render(ed.parentEl || document.body);
39941                 }
39942                 ed.field.reset();
39943                
39944                 cell.hide();
39945                 
39946                 (function(){ // complex but required for focus issues in safari, ie and opera
39947                     ed.row = row;
39948                     ed.col = col;
39949                     ed.record = r;
39950                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39951                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39952                     this.activeEditor = ed;
39953                     var v = r.data[field];
39954                     ed.startEdit(this.view.getCell(row, col), v);
39955                     // combo's with 'displayField and name set
39956                     if (ed.field.displayField && ed.field.name) {
39957                         ed.field.el.dom.value = r.data[ed.field.name];
39958                     }
39959                     
39960                     
39961                 }).defer(50, this);
39962             }
39963         }
39964     },
39965         
39966     /**
39967      * Stops any active editing
39968      */
39969     stopEditing : function(){
39970         if(this.activeEditor){
39971             this.activeEditor.completeEdit();
39972         }
39973         this.activeEditor = null;
39974     },
39975         
39976          /**
39977      * Called to get grid's drag proxy text, by default returns this.ddText.
39978      * @return {String}
39979      */
39980     getDragDropText : function(){
39981         var count = this.selModel.getSelectedCell() ? 1 : 0;
39982         return String.format(this.ddText, count, count == 1 ? '' : 's');
39983     }
39984         
39985 });/*
39986  * Based on:
39987  * Ext JS Library 1.1.1
39988  * Copyright(c) 2006-2007, Ext JS, LLC.
39989  *
39990  * Originally Released Under LGPL - original licence link has changed is not relivant.
39991  *
39992  * Fork - LGPL
39993  * <script type="text/javascript">
39994  */
39995
39996 // private - not really -- you end up using it !
39997 // This is a support class used internally by the Grid components
39998
39999 /**
40000  * @class Roo.grid.GridEditor
40001  * @extends Roo.Editor
40002  * Class for creating and editable grid elements.
40003  * @param {Object} config any settings (must include field)
40004  */
40005 Roo.grid.GridEditor = function(field, config){
40006     if (!config && field.field) {
40007         config = field;
40008         field = Roo.factory(config.field, Roo.form);
40009     }
40010     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40011     field.monitorTab = false;
40012 };
40013
40014 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40015     
40016     /**
40017      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40018      */
40019     
40020     alignment: "tl-tl",
40021     autoSize: "width",
40022     hideEl : false,
40023     cls: "x-small-editor x-grid-editor",
40024     shim:false,
40025     shadow:"frame"
40026 });/*
40027  * Based on:
40028  * Ext JS Library 1.1.1
40029  * Copyright(c) 2006-2007, Ext JS, LLC.
40030  *
40031  * Originally Released Under LGPL - original licence link has changed is not relivant.
40032  *
40033  * Fork - LGPL
40034  * <script type="text/javascript">
40035  */
40036   
40037
40038   
40039 Roo.grid.PropertyRecord = Roo.data.Record.create([
40040     {name:'name',type:'string'},  'value'
40041 ]);
40042
40043
40044 Roo.grid.PropertyStore = function(grid, source){
40045     this.grid = grid;
40046     this.store = new Roo.data.Store({
40047         recordType : Roo.grid.PropertyRecord
40048     });
40049     this.store.on('update', this.onUpdate,  this);
40050     if(source){
40051         this.setSource(source);
40052     }
40053     Roo.grid.PropertyStore.superclass.constructor.call(this);
40054 };
40055
40056
40057
40058 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40059     setSource : function(o){
40060         this.source = o;
40061         this.store.removeAll();
40062         var data = [];
40063         for(var k in o){
40064             if(this.isEditableValue(o[k])){
40065                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40066             }
40067         }
40068         this.store.loadRecords({records: data}, {}, true);
40069     },
40070
40071     onUpdate : function(ds, record, type){
40072         if(type == Roo.data.Record.EDIT){
40073             var v = record.data['value'];
40074             var oldValue = record.modified['value'];
40075             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40076                 this.source[record.id] = v;
40077                 record.commit();
40078                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40079             }else{
40080                 record.reject();
40081             }
40082         }
40083     },
40084
40085     getProperty : function(row){
40086        return this.store.getAt(row);
40087     },
40088
40089     isEditableValue: function(val){
40090         if(val && val instanceof Date){
40091             return true;
40092         }else if(typeof val == 'object' || typeof val == 'function'){
40093             return false;
40094         }
40095         return true;
40096     },
40097
40098     setValue : function(prop, value){
40099         this.source[prop] = value;
40100         this.store.getById(prop).set('value', value);
40101     },
40102
40103     getSource : function(){
40104         return this.source;
40105     }
40106 });
40107
40108 Roo.grid.PropertyColumnModel = function(grid, store){
40109     this.grid = grid;
40110     var g = Roo.grid;
40111     g.PropertyColumnModel.superclass.constructor.call(this, [
40112         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40113         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40114     ]);
40115     this.store = store;
40116     this.bselect = Roo.DomHelper.append(document.body, {
40117         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40118             {tag: 'option', value: 'true', html: 'true'},
40119             {tag: 'option', value: 'false', html: 'false'}
40120         ]
40121     });
40122     Roo.id(this.bselect);
40123     var f = Roo.form;
40124     this.editors = {
40125         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40126         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40127         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40128         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40129         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40130     };
40131     this.renderCellDelegate = this.renderCell.createDelegate(this);
40132     this.renderPropDelegate = this.renderProp.createDelegate(this);
40133 };
40134
40135 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40136     
40137     
40138     nameText : 'Name',
40139     valueText : 'Value',
40140     
40141     dateFormat : 'm/j/Y',
40142     
40143     
40144     renderDate : function(dateVal){
40145         return dateVal.dateFormat(this.dateFormat);
40146     },
40147
40148     renderBool : function(bVal){
40149         return bVal ? 'true' : 'false';
40150     },
40151
40152     isCellEditable : function(colIndex, rowIndex){
40153         return colIndex == 1;
40154     },
40155
40156     getRenderer : function(col){
40157         return col == 1 ?
40158             this.renderCellDelegate : this.renderPropDelegate;
40159     },
40160
40161     renderProp : function(v){
40162         return this.getPropertyName(v);
40163     },
40164
40165     renderCell : function(val){
40166         var rv = val;
40167         if(val instanceof Date){
40168             rv = this.renderDate(val);
40169         }else if(typeof val == 'boolean'){
40170             rv = this.renderBool(val);
40171         }
40172         return Roo.util.Format.htmlEncode(rv);
40173     },
40174
40175     getPropertyName : function(name){
40176         var pn = this.grid.propertyNames;
40177         return pn && pn[name] ? pn[name] : name;
40178     },
40179
40180     getCellEditor : function(colIndex, rowIndex){
40181         var p = this.store.getProperty(rowIndex);
40182         var n = p.data['name'], val = p.data['value'];
40183         
40184         if(typeof(this.grid.customEditors[n]) == 'string'){
40185             return this.editors[this.grid.customEditors[n]];
40186         }
40187         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40188             return this.grid.customEditors[n];
40189         }
40190         if(val instanceof Date){
40191             return this.editors['date'];
40192         }else if(typeof val == 'number'){
40193             return this.editors['number'];
40194         }else if(typeof val == 'boolean'){
40195             return this.editors['boolean'];
40196         }else{
40197             return this.editors['string'];
40198         }
40199     }
40200 });
40201
40202 /**
40203  * @class Roo.grid.PropertyGrid
40204  * @extends Roo.grid.EditorGrid
40205  * This class represents the  interface of a component based property grid control.
40206  * <br><br>Usage:<pre><code>
40207  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40208       
40209  });
40210  // set any options
40211  grid.render();
40212  * </code></pre>
40213   
40214  * @constructor
40215  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40216  * The container MUST have some type of size defined for the grid to fill. The container will be
40217  * automatically set to position relative if it isn't already.
40218  * @param {Object} config A config object that sets properties on this grid.
40219  */
40220 Roo.grid.PropertyGrid = function(container, config){
40221     config = config || {};
40222     var store = new Roo.grid.PropertyStore(this);
40223     this.store = store;
40224     var cm = new Roo.grid.PropertyColumnModel(this, store);
40225     store.store.sort('name', 'ASC');
40226     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40227         ds: store.store,
40228         cm: cm,
40229         enableColLock:false,
40230         enableColumnMove:false,
40231         stripeRows:false,
40232         trackMouseOver: false,
40233         clicksToEdit:1
40234     }, config));
40235     this.getGridEl().addClass('x-props-grid');
40236     this.lastEditRow = null;
40237     this.on('columnresize', this.onColumnResize, this);
40238     this.addEvents({
40239          /**
40240              * @event beforepropertychange
40241              * Fires before a property changes (return false to stop?)
40242              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40243              * @param {String} id Record Id
40244              * @param {String} newval New Value
40245          * @param {String} oldval Old Value
40246              */
40247         "beforepropertychange": true,
40248         /**
40249              * @event propertychange
40250              * Fires after a property changes
40251              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40252              * @param {String} id Record Id
40253              * @param {String} newval New Value
40254          * @param {String} oldval Old Value
40255              */
40256         "propertychange": true
40257     });
40258     this.customEditors = this.customEditors || {};
40259 };
40260 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40261     
40262      /**
40263      * @cfg {Object} customEditors map of colnames=> custom editors.
40264      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40265      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40266      * false disables editing of the field.
40267          */
40268     
40269       /**
40270      * @cfg {Object} propertyNames map of property Names to their displayed value
40271          */
40272     
40273     render : function(){
40274         Roo.grid.PropertyGrid.superclass.render.call(this);
40275         this.autoSize.defer(100, this);
40276     },
40277
40278     autoSize : function(){
40279         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40280         if(this.view){
40281             this.view.fitColumns();
40282         }
40283     },
40284
40285     onColumnResize : function(){
40286         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40287         this.autoSize();
40288     },
40289     /**
40290      * Sets the data for the Grid
40291      * accepts a Key => Value object of all the elements avaiable.
40292      * @param {Object} data  to appear in grid.
40293      */
40294     setSource : function(source){
40295         this.store.setSource(source);
40296         //this.autoSize();
40297     },
40298     /**
40299      * Gets all the data from the grid.
40300      * @return {Object} data  data stored in grid
40301      */
40302     getSource : function(){
40303         return this.store.getSource();
40304     }
40305 });/*
40306   
40307  * Licence LGPL
40308  
40309  */
40310  
40311 /**
40312  * @class Roo.grid.Calendar
40313  * @extends Roo.util.Grid
40314  * This class extends the Grid to provide a calendar widget
40315  * <br><br>Usage:<pre><code>
40316  var grid = new Roo.grid.Calendar("my-container-id", {
40317      ds: myDataStore,
40318      cm: myColModel,
40319      selModel: mySelectionModel,
40320      autoSizeColumns: true,
40321      monitorWindowResize: false,
40322      trackMouseOver: true
40323      eventstore : real data store..
40324  });
40325  // set any options
40326  grid.render();
40327   
40328   * @constructor
40329  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40330  * The container MUST have some type of size defined for the grid to fill. The container will be
40331  * automatically set to position relative if it isn't already.
40332  * @param {Object} config A config object that sets properties on this grid.
40333  */
40334 Roo.grid.Calendar = function(container, config){
40335         // initialize the container
40336         this.container = Roo.get(container);
40337         this.container.update("");
40338         this.container.setStyle("overflow", "hidden");
40339     this.container.addClass('x-grid-container');
40340
40341     this.id = this.container.id;
40342
40343     Roo.apply(this, config);
40344     // check and correct shorthanded configs
40345     
40346     var rows = [];
40347     var d =1;
40348     for (var r = 0;r < 6;r++) {
40349         
40350         rows[r]=[];
40351         for (var c =0;c < 7;c++) {
40352             rows[r][c]= '';
40353         }
40354     }
40355     if (this.eventStore) {
40356         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40357         this.eventStore.on('load',this.onLoad, this);
40358         this.eventStore.on('beforeload',this.clearEvents, this);
40359          
40360     }
40361     
40362     this.dataSource = new Roo.data.Store({
40363             proxy: new Roo.data.MemoryProxy(rows),
40364             reader: new Roo.data.ArrayReader({}, [
40365                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40366     });
40367
40368     this.dataSource.load();
40369     this.ds = this.dataSource;
40370     this.ds.xmodule = this.xmodule || false;
40371     
40372     
40373     var cellRender = function(v,x,r)
40374     {
40375         return String.format(
40376             '<div class="fc-day  fc-widget-content"><div>' +
40377                 '<div class="fc-event-container"></div>' +
40378                 '<div class="fc-day-number">{0}</div>'+
40379                 
40380                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40381             '</div></div>', v);
40382     
40383     }
40384     
40385     
40386     this.colModel = new Roo.grid.ColumnModel( [
40387         {
40388             xtype: 'ColumnModel',
40389             xns: Roo.grid,
40390             dataIndex : 'weekday0',
40391             header : 'Sunday',
40392             renderer : cellRender
40393         },
40394         {
40395             xtype: 'ColumnModel',
40396             xns: Roo.grid,
40397             dataIndex : 'weekday1',
40398             header : 'Monday',
40399             renderer : cellRender
40400         },
40401         {
40402             xtype: 'ColumnModel',
40403             xns: Roo.grid,
40404             dataIndex : 'weekday2',
40405             header : 'Tuesday',
40406             renderer : cellRender
40407         },
40408         {
40409             xtype: 'ColumnModel',
40410             xns: Roo.grid,
40411             dataIndex : 'weekday3',
40412             header : 'Wednesday',
40413             renderer : cellRender
40414         },
40415         {
40416             xtype: 'ColumnModel',
40417             xns: Roo.grid,
40418             dataIndex : 'weekday4',
40419             header : 'Thursday',
40420             renderer : cellRender
40421         },
40422         {
40423             xtype: 'ColumnModel',
40424             xns: Roo.grid,
40425             dataIndex : 'weekday5',
40426             header : 'Friday',
40427             renderer : cellRender
40428         },
40429         {
40430             xtype: 'ColumnModel',
40431             xns: Roo.grid,
40432             dataIndex : 'weekday6',
40433             header : 'Saturday',
40434             renderer : cellRender
40435         }
40436     ]);
40437     this.cm = this.colModel;
40438     this.cm.xmodule = this.xmodule || false;
40439  
40440         
40441           
40442     //this.selModel = new Roo.grid.CellSelectionModel();
40443     //this.sm = this.selModel;
40444     //this.selModel.init(this);
40445     
40446     
40447     if(this.width){
40448         this.container.setWidth(this.width);
40449     }
40450
40451     if(this.height){
40452         this.container.setHeight(this.height);
40453     }
40454     /** @private */
40455         this.addEvents({
40456         // raw events
40457         /**
40458          * @event click
40459          * The raw click event for the entire grid.
40460          * @param {Roo.EventObject} e
40461          */
40462         "click" : true,
40463         /**
40464          * @event dblclick
40465          * The raw dblclick event for the entire grid.
40466          * @param {Roo.EventObject} e
40467          */
40468         "dblclick" : true,
40469         /**
40470          * @event contextmenu
40471          * The raw contextmenu event for the entire grid.
40472          * @param {Roo.EventObject} e
40473          */
40474         "contextmenu" : true,
40475         /**
40476          * @event mousedown
40477          * The raw mousedown event for the entire grid.
40478          * @param {Roo.EventObject} e
40479          */
40480         "mousedown" : true,
40481         /**
40482          * @event mouseup
40483          * The raw mouseup event for the entire grid.
40484          * @param {Roo.EventObject} e
40485          */
40486         "mouseup" : true,
40487         /**
40488          * @event mouseover
40489          * The raw mouseover event for the entire grid.
40490          * @param {Roo.EventObject} e
40491          */
40492         "mouseover" : true,
40493         /**
40494          * @event mouseout
40495          * The raw mouseout event for the entire grid.
40496          * @param {Roo.EventObject} e
40497          */
40498         "mouseout" : true,
40499         /**
40500          * @event keypress
40501          * The raw keypress event for the entire grid.
40502          * @param {Roo.EventObject} e
40503          */
40504         "keypress" : true,
40505         /**
40506          * @event keydown
40507          * The raw keydown event for the entire grid.
40508          * @param {Roo.EventObject} e
40509          */
40510         "keydown" : true,
40511
40512         // custom events
40513
40514         /**
40515          * @event cellclick
40516          * Fires when a cell is clicked
40517          * @param {Grid} this
40518          * @param {Number} rowIndex
40519          * @param {Number} columnIndex
40520          * @param {Roo.EventObject} e
40521          */
40522         "cellclick" : true,
40523         /**
40524          * @event celldblclick
40525          * Fires when a cell is double clicked
40526          * @param {Grid} this
40527          * @param {Number} rowIndex
40528          * @param {Number} columnIndex
40529          * @param {Roo.EventObject} e
40530          */
40531         "celldblclick" : true,
40532         /**
40533          * @event rowclick
40534          * Fires when a row is clicked
40535          * @param {Grid} this
40536          * @param {Number} rowIndex
40537          * @param {Roo.EventObject} e
40538          */
40539         "rowclick" : true,
40540         /**
40541          * @event rowdblclick
40542          * Fires when a row is double clicked
40543          * @param {Grid} this
40544          * @param {Number} rowIndex
40545          * @param {Roo.EventObject} e
40546          */
40547         "rowdblclick" : true,
40548         /**
40549          * @event headerclick
40550          * Fires when a header is clicked
40551          * @param {Grid} this
40552          * @param {Number} columnIndex
40553          * @param {Roo.EventObject} e
40554          */
40555         "headerclick" : true,
40556         /**
40557          * @event headerdblclick
40558          * Fires when a header cell is double clicked
40559          * @param {Grid} this
40560          * @param {Number} columnIndex
40561          * @param {Roo.EventObject} e
40562          */
40563         "headerdblclick" : true,
40564         /**
40565          * @event rowcontextmenu
40566          * Fires when a row is right clicked
40567          * @param {Grid} this
40568          * @param {Number} rowIndex
40569          * @param {Roo.EventObject} e
40570          */
40571         "rowcontextmenu" : true,
40572         /**
40573          * @event cellcontextmenu
40574          * Fires when a cell is right clicked
40575          * @param {Grid} this
40576          * @param {Number} rowIndex
40577          * @param {Number} cellIndex
40578          * @param {Roo.EventObject} e
40579          */
40580          "cellcontextmenu" : true,
40581         /**
40582          * @event headercontextmenu
40583          * Fires when a header is right clicked
40584          * @param {Grid} this
40585          * @param {Number} columnIndex
40586          * @param {Roo.EventObject} e
40587          */
40588         "headercontextmenu" : true,
40589         /**
40590          * @event bodyscroll
40591          * Fires when the body element is scrolled
40592          * @param {Number} scrollLeft
40593          * @param {Number} scrollTop
40594          */
40595         "bodyscroll" : true,
40596         /**
40597          * @event columnresize
40598          * Fires when the user resizes a column
40599          * @param {Number} columnIndex
40600          * @param {Number} newSize
40601          */
40602         "columnresize" : true,
40603         /**
40604          * @event columnmove
40605          * Fires when the user moves a column
40606          * @param {Number} oldIndex
40607          * @param {Number} newIndex
40608          */
40609         "columnmove" : true,
40610         /**
40611          * @event startdrag
40612          * Fires when row(s) start being dragged
40613          * @param {Grid} this
40614          * @param {Roo.GridDD} dd The drag drop object
40615          * @param {event} e The raw browser event
40616          */
40617         "startdrag" : true,
40618         /**
40619          * @event enddrag
40620          * Fires when a drag operation is complete
40621          * @param {Grid} this
40622          * @param {Roo.GridDD} dd The drag drop object
40623          * @param {event} e The raw browser event
40624          */
40625         "enddrag" : true,
40626         /**
40627          * @event dragdrop
40628          * Fires when dragged row(s) are dropped on a valid DD target
40629          * @param {Grid} this
40630          * @param {Roo.GridDD} dd The drag drop object
40631          * @param {String} targetId The target drag drop object
40632          * @param {event} e The raw browser event
40633          */
40634         "dragdrop" : true,
40635         /**
40636          * @event dragover
40637          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40638          * @param {Grid} this
40639          * @param {Roo.GridDD} dd The drag drop object
40640          * @param {String} targetId The target drag drop object
40641          * @param {event} e The raw browser event
40642          */
40643         "dragover" : true,
40644         /**
40645          * @event dragenter
40646          *  Fires when the dragged row(s) first cross another DD target while being dragged
40647          * @param {Grid} this
40648          * @param {Roo.GridDD} dd The drag drop object
40649          * @param {String} targetId The target drag drop object
40650          * @param {event} e The raw browser event
40651          */
40652         "dragenter" : true,
40653         /**
40654          * @event dragout
40655          * Fires when the dragged row(s) leave another DD target while being dragged
40656          * @param {Grid} this
40657          * @param {Roo.GridDD} dd The drag drop object
40658          * @param {String} targetId The target drag drop object
40659          * @param {event} e The raw browser event
40660          */
40661         "dragout" : true,
40662         /**
40663          * @event rowclass
40664          * Fires when a row is rendered, so you can change add a style to it.
40665          * @param {GridView} gridview   The grid view
40666          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40667          */
40668         'rowclass' : true,
40669
40670         /**
40671          * @event render
40672          * Fires when the grid is rendered
40673          * @param {Grid} grid
40674          */
40675         'render' : true,
40676             /**
40677              * @event select
40678              * Fires when a date is selected
40679              * @param {DatePicker} this
40680              * @param {Date} date The selected date
40681              */
40682         'select': true,
40683         /**
40684              * @event monthchange
40685              * Fires when the displayed month changes 
40686              * @param {DatePicker} this
40687              * @param {Date} date The selected month
40688              */
40689         'monthchange': true,
40690         /**
40691              * @event evententer
40692              * Fires when mouse over an event
40693              * @param {Calendar} this
40694              * @param {event} Event
40695              */
40696         'evententer': true,
40697         /**
40698              * @event eventleave
40699              * Fires when the mouse leaves an
40700              * @param {Calendar} this
40701              * @param {event}
40702              */
40703         'eventleave': true,
40704         /**
40705              * @event eventclick
40706              * Fires when the mouse click an
40707              * @param {Calendar} this
40708              * @param {event}
40709              */
40710         'eventclick': true,
40711         /**
40712              * @event eventrender
40713              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40714              * @param {Calendar} this
40715              * @param {data} data to be modified
40716              */
40717         'eventrender': true
40718         
40719     });
40720
40721     Roo.grid.Grid.superclass.constructor.call(this);
40722     this.on('render', function() {
40723         this.view.el.addClass('x-grid-cal'); 
40724         
40725         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40726
40727     },this);
40728     
40729     if (!Roo.grid.Calendar.style) {
40730         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40731             
40732             
40733             '.x-grid-cal .x-grid-col' :  {
40734                 height: 'auto !important',
40735                 'vertical-align': 'top'
40736             },
40737             '.x-grid-cal  .fc-event-hori' : {
40738                 height: '14px'
40739             }
40740              
40741             
40742         }, Roo.id());
40743     }
40744
40745     
40746     
40747 };
40748 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40749     /**
40750      * @cfg {Store} eventStore The store that loads events.
40751      */
40752     eventStore : 25,
40753
40754      
40755     activeDate : false,
40756     startDay : 0,
40757     autoWidth : true,
40758     monitorWindowResize : false,
40759
40760     
40761     resizeColumns : function() {
40762         var col = (this.view.el.getWidth() / 7) - 3;
40763         // loop through cols, and setWidth
40764         for(var i =0 ; i < 7 ; i++){
40765             this.cm.setColumnWidth(i, col);
40766         }
40767     },
40768      setDate :function(date) {
40769         
40770         Roo.log('setDate?');
40771         
40772         this.resizeColumns();
40773         var vd = this.activeDate;
40774         this.activeDate = date;
40775 //        if(vd && this.el){
40776 //            var t = date.getTime();
40777 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40778 //                Roo.log('using add remove');
40779 //                
40780 //                this.fireEvent('monthchange', this, date);
40781 //                
40782 //                this.cells.removeClass("fc-state-highlight");
40783 //                this.cells.each(function(c){
40784 //                   if(c.dateValue == t){
40785 //                       c.addClass("fc-state-highlight");
40786 //                       setTimeout(function(){
40787 //                            try{c.dom.firstChild.focus();}catch(e){}
40788 //                       }, 50);
40789 //                       return false;
40790 //                   }
40791 //                   return true;
40792 //                });
40793 //                return;
40794 //            }
40795 //        }
40796         
40797         var days = date.getDaysInMonth();
40798         
40799         var firstOfMonth = date.getFirstDateOfMonth();
40800         var startingPos = firstOfMonth.getDay()-this.startDay;
40801         
40802         if(startingPos < this.startDay){
40803             startingPos += 7;
40804         }
40805         
40806         var pm = date.add(Date.MONTH, -1);
40807         var prevStart = pm.getDaysInMonth()-startingPos;
40808 //        
40809         
40810         
40811         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40812         
40813         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40814         //this.cells.addClassOnOver('fc-state-hover');
40815         
40816         var cells = this.cells.elements;
40817         var textEls = this.textNodes;
40818         
40819         //Roo.each(cells, function(cell){
40820         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40821         //});
40822         
40823         days += startingPos;
40824
40825         // convert everything to numbers so it's fast
40826         var day = 86400000;
40827         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40828         //Roo.log(d);
40829         //Roo.log(pm);
40830         //Roo.log(prevStart);
40831         
40832         var today = new Date().clearTime().getTime();
40833         var sel = date.clearTime().getTime();
40834         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40835         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40836         var ddMatch = this.disabledDatesRE;
40837         var ddText = this.disabledDatesText;
40838         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40839         var ddaysText = this.disabledDaysText;
40840         var format = this.format;
40841         
40842         var setCellClass = function(cal, cell){
40843             
40844             //Roo.log('set Cell Class');
40845             cell.title = "";
40846             var t = d.getTime();
40847             
40848             //Roo.log(d);
40849             
40850             
40851             cell.dateValue = t;
40852             if(t == today){
40853                 cell.className += " fc-today";
40854                 cell.className += " fc-state-highlight";
40855                 cell.title = cal.todayText;
40856             }
40857             if(t == sel){
40858                 // disable highlight in other month..
40859                 cell.className += " fc-state-highlight";
40860                 
40861             }
40862             // disabling
40863             if(t < min) {
40864                 //cell.className = " fc-state-disabled";
40865                 cell.title = cal.minText;
40866                 return;
40867             }
40868             if(t > max) {
40869                 //cell.className = " fc-state-disabled";
40870                 cell.title = cal.maxText;
40871                 return;
40872             }
40873             if(ddays){
40874                 if(ddays.indexOf(d.getDay()) != -1){
40875                     // cell.title = ddaysText;
40876                    // cell.className = " fc-state-disabled";
40877                 }
40878             }
40879             if(ddMatch && format){
40880                 var fvalue = d.dateFormat(format);
40881                 if(ddMatch.test(fvalue)){
40882                     cell.title = ddText.replace("%0", fvalue);
40883                    cell.className = " fc-state-disabled";
40884                 }
40885             }
40886             
40887             if (!cell.initialClassName) {
40888                 cell.initialClassName = cell.dom.className;
40889             }
40890             
40891             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40892         };
40893
40894         var i = 0;
40895         
40896         for(; i < startingPos; i++) {
40897             cells[i].dayName =  (++prevStart);
40898             Roo.log(textEls[i]);
40899             d.setDate(d.getDate()+1);
40900             
40901             //cells[i].className = "fc-past fc-other-month";
40902             setCellClass(this, cells[i]);
40903         }
40904         
40905         var intDay = 0;
40906         
40907         for(; i < days; i++){
40908             intDay = i - startingPos + 1;
40909             cells[i].dayName =  (intDay);
40910             d.setDate(d.getDate()+1);
40911             
40912             cells[i].className = ''; // "x-date-active";
40913             setCellClass(this, cells[i]);
40914         }
40915         var extraDays = 0;
40916         
40917         for(; i < 42; i++) {
40918             //textEls[i].innerHTML = (++extraDays);
40919             
40920             d.setDate(d.getDate()+1);
40921             cells[i].dayName = (++extraDays);
40922             cells[i].className = "fc-future fc-other-month";
40923             setCellClass(this, cells[i]);
40924         }
40925         
40926         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40927         
40928         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40929         
40930         // this will cause all the cells to mis
40931         var rows= [];
40932         var i =0;
40933         for (var r = 0;r < 6;r++) {
40934             for (var c =0;c < 7;c++) {
40935                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40936             }    
40937         }
40938         
40939         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40940         for(i=0;i<cells.length;i++) {
40941             
40942             this.cells.elements[i].dayName = cells[i].dayName ;
40943             this.cells.elements[i].className = cells[i].className;
40944             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40945             this.cells.elements[i].title = cells[i].title ;
40946             this.cells.elements[i].dateValue = cells[i].dateValue ;
40947         }
40948         
40949         
40950         
40951         
40952         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40953         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40954         
40955         ////if(totalRows != 6){
40956             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40957            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40958        // }
40959         
40960         this.fireEvent('monthchange', this, date);
40961         
40962         
40963     },
40964  /**
40965      * Returns the grid's SelectionModel.
40966      * @return {SelectionModel}
40967      */
40968     getSelectionModel : function(){
40969         if(!this.selModel){
40970             this.selModel = new Roo.grid.CellSelectionModel();
40971         }
40972         return this.selModel;
40973     },
40974
40975     load: function() {
40976         this.eventStore.load()
40977         
40978         
40979         
40980     },
40981     
40982     findCell : function(dt) {
40983         dt = dt.clearTime().getTime();
40984         var ret = false;
40985         this.cells.each(function(c){
40986             //Roo.log("check " +c.dateValue + '?=' + dt);
40987             if(c.dateValue == dt){
40988                 ret = c;
40989                 return false;
40990             }
40991             return true;
40992         });
40993         
40994         return ret;
40995     },
40996     
40997     findCells : function(rec) {
40998         var s = rec.data.start_dt.clone().clearTime().getTime();
40999        // Roo.log(s);
41000         var e= rec.data.end_dt.clone().clearTime().getTime();
41001        // Roo.log(e);
41002         var ret = [];
41003         this.cells.each(function(c){
41004              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41005             
41006             if(c.dateValue > e){
41007                 return ;
41008             }
41009             if(c.dateValue < s){
41010                 return ;
41011             }
41012             ret.push(c);
41013         });
41014         
41015         return ret;    
41016     },
41017     
41018     findBestRow: function(cells)
41019     {
41020         var ret = 0;
41021         
41022         for (var i =0 ; i < cells.length;i++) {
41023             ret  = Math.max(cells[i].rows || 0,ret);
41024         }
41025         return ret;
41026         
41027     },
41028     
41029     
41030     addItem : function(rec)
41031     {
41032         // look for vertical location slot in
41033         var cells = this.findCells(rec);
41034         
41035         rec.row = this.findBestRow(cells);
41036         
41037         // work out the location.
41038         
41039         var crow = false;
41040         var rows = [];
41041         for(var i =0; i < cells.length; i++) {
41042             if (!crow) {
41043                 crow = {
41044                     start : cells[i],
41045                     end :  cells[i]
41046                 };
41047                 continue;
41048             }
41049             if (crow.start.getY() == cells[i].getY()) {
41050                 // on same row.
41051                 crow.end = cells[i];
41052                 continue;
41053             }
41054             // different row.
41055             rows.push(crow);
41056             crow = {
41057                 start: cells[i],
41058                 end : cells[i]
41059             };
41060             
41061         }
41062         
41063         rows.push(crow);
41064         rec.els = [];
41065         rec.rows = rows;
41066         rec.cells = cells;
41067         for (var i = 0; i < cells.length;i++) {
41068             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41069             
41070         }
41071         
41072         
41073     },
41074     
41075     clearEvents: function() {
41076         
41077         if (!this.eventStore.getCount()) {
41078             return;
41079         }
41080         // reset number of rows in cells.
41081         Roo.each(this.cells.elements, function(c){
41082             c.rows = 0;
41083         });
41084         
41085         this.eventStore.each(function(e) {
41086             this.clearEvent(e);
41087         },this);
41088         
41089     },
41090     
41091     clearEvent : function(ev)
41092     {
41093         if (ev.els) {
41094             Roo.each(ev.els, function(el) {
41095                 el.un('mouseenter' ,this.onEventEnter, this);
41096                 el.un('mouseleave' ,this.onEventLeave, this);
41097                 el.remove();
41098             },this);
41099             ev.els = [];
41100         }
41101     },
41102     
41103     
41104     renderEvent : function(ev,ctr) {
41105         if (!ctr) {
41106              ctr = this.view.el.select('.fc-event-container',true).first();
41107         }
41108         
41109          
41110         this.clearEvent(ev);
41111             //code
41112        
41113         
41114         
41115         ev.els = [];
41116         var cells = ev.cells;
41117         var rows = ev.rows;
41118         this.fireEvent('eventrender', this, ev);
41119         
41120         for(var i =0; i < rows.length; i++) {
41121             
41122             cls = '';
41123             if (i == 0) {
41124                 cls += ' fc-event-start';
41125             }
41126             if ((i+1) == rows.length) {
41127                 cls += ' fc-event-end';
41128             }
41129             
41130             //Roo.log(ev.data);
41131             // how many rows should it span..
41132             var cg = this.eventTmpl.append(ctr,Roo.apply({
41133                 fccls : cls
41134                 
41135             }, ev.data) , true);
41136             
41137             
41138             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41139             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41140             cg.on('click', this.onEventClick, this, ev);
41141             
41142             ev.els.push(cg);
41143             
41144             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41145             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41146             //Roo.log(cg);
41147              
41148             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41149             cg.setWidth(ebox.right - sbox.x -2);
41150         }
41151     },
41152     
41153     renderEvents: function()
41154     {   
41155         // first make sure there is enough space..
41156         
41157         if (!this.eventTmpl) {
41158             this.eventTmpl = new Roo.Template(
41159                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41160                     '<div class="fc-event-inner">' +
41161                         '<span class="fc-event-time">{time}</span>' +
41162                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41163                     '</div>' +
41164                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41165                 '</div>'
41166             );
41167                 
41168         }
41169                
41170         
41171         
41172         this.cells.each(function(c) {
41173             //Roo.log(c.select('.fc-day-content div',true).first());
41174             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41175         });
41176         
41177         var ctr = this.view.el.select('.fc-event-container',true).first();
41178         
41179         var cls;
41180         this.eventStore.each(function(ev){
41181             
41182             this.renderEvent(ev);
41183              
41184              
41185         }, this);
41186         this.view.layout();
41187         
41188     },
41189     
41190     onEventEnter: function (e, el,event,d) {
41191         this.fireEvent('evententer', this, el, event);
41192     },
41193     
41194     onEventLeave: function (e, el,event,d) {
41195         this.fireEvent('eventleave', this, el, event);
41196     },
41197     
41198     onEventClick: function (e, el,event,d) {
41199         this.fireEvent('eventclick', this, el, event);
41200     },
41201     
41202     onMonthChange: function () {
41203         this.store.load();
41204     },
41205     
41206     onLoad: function () {
41207         
41208         //Roo.log('calendar onload');
41209 //         
41210         if(this.eventStore.getCount() > 0){
41211             
41212            
41213             
41214             this.eventStore.each(function(d){
41215                 
41216                 
41217                 // FIXME..
41218                 var add =   d.data;
41219                 if (typeof(add.end_dt) == 'undefined')  {
41220                     Roo.log("Missing End time in calendar data: ");
41221                     Roo.log(d);
41222                     return;
41223                 }
41224                 if (typeof(add.start_dt) == 'undefined')  {
41225                     Roo.log("Missing Start time in calendar data: ");
41226                     Roo.log(d);
41227                     return;
41228                 }
41229                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41230                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41231                 add.id = add.id || d.id;
41232                 add.title = add.title || '??';
41233                 
41234                 this.addItem(d);
41235                 
41236              
41237             },this);
41238         }
41239         
41240         this.renderEvents();
41241     }
41242     
41243
41244 });
41245 /*
41246  grid : {
41247                 xtype: 'Grid',
41248                 xns: Roo.grid,
41249                 listeners : {
41250                     render : function ()
41251                     {
41252                         _this.grid = this;
41253                         
41254                         if (!this.view.el.hasClass('course-timesheet')) {
41255                             this.view.el.addClass('course-timesheet');
41256                         }
41257                         if (this.tsStyle) {
41258                             this.ds.load({});
41259                             return; 
41260                         }
41261                         Roo.log('width');
41262                         Roo.log(_this.grid.view.el.getWidth());
41263                         
41264                         
41265                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41266                             '.course-timesheet .x-grid-row' : {
41267                                 height: '80px'
41268                             },
41269                             '.x-grid-row td' : {
41270                                 'vertical-align' : 0
41271                             },
41272                             '.course-edit-link' : {
41273                                 'color' : 'blue',
41274                                 'text-overflow' : 'ellipsis',
41275                                 'overflow' : 'hidden',
41276                                 'white-space' : 'nowrap',
41277                                 'cursor' : 'pointer'
41278                             },
41279                             '.sub-link' : {
41280                                 'color' : 'green'
41281                             },
41282                             '.de-act-sup-link' : {
41283                                 'color' : 'purple',
41284                                 'text-decoration' : 'line-through'
41285                             },
41286                             '.de-act-link' : {
41287                                 'color' : 'red',
41288                                 'text-decoration' : 'line-through'
41289                             },
41290                             '.course-timesheet .course-highlight' : {
41291                                 'border-top-style': 'dashed !important',
41292                                 'border-bottom-bottom': 'dashed !important'
41293                             },
41294                             '.course-timesheet .course-item' : {
41295                                 'font-family'   : 'tahoma, arial, helvetica',
41296                                 'font-size'     : '11px',
41297                                 'overflow'      : 'hidden',
41298                                 'padding-left'  : '10px',
41299                                 'padding-right' : '10px',
41300                                 'padding-top' : '10px' 
41301                             }
41302                             
41303                         }, Roo.id());
41304                                 this.ds.load({});
41305                     }
41306                 },
41307                 autoWidth : true,
41308                 monitorWindowResize : false,
41309                 cellrenderer : function(v,x,r)
41310                 {
41311                     return v;
41312                 },
41313                 sm : {
41314                     xtype: 'CellSelectionModel',
41315                     xns: Roo.grid
41316                 },
41317                 dataSource : {
41318                     xtype: 'Store',
41319                     xns: Roo.data,
41320                     listeners : {
41321                         beforeload : function (_self, options)
41322                         {
41323                             options.params = options.params || {};
41324                             options.params._month = _this.monthField.getValue();
41325                             options.params.limit = 9999;
41326                             options.params['sort'] = 'when_dt';    
41327                             options.params['dir'] = 'ASC';    
41328                             this.proxy.loadResponse = this.loadResponse;
41329                             Roo.log("load?");
41330                             //this.addColumns();
41331                         },
41332                         load : function (_self, records, options)
41333                         {
41334                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41335                                 // if you click on the translation.. you can edit it...
41336                                 var el = Roo.get(this);
41337                                 var id = el.dom.getAttribute('data-id');
41338                                 var d = el.dom.getAttribute('data-date');
41339                                 var t = el.dom.getAttribute('data-time');
41340                                 //var id = this.child('span').dom.textContent;
41341                                 
41342                                 //Roo.log(this);
41343                                 Pman.Dialog.CourseCalendar.show({
41344                                     id : id,
41345                                     when_d : d,
41346                                     when_t : t,
41347                                     productitem_active : id ? 1 : 0
41348                                 }, function() {
41349                                     _this.grid.ds.load({});
41350                                 });
41351                            
41352                            });
41353                            
41354                            _this.panel.fireEvent('resize', [ '', '' ]);
41355                         }
41356                     },
41357                     loadResponse : function(o, success, response){
41358                             // this is overridden on before load..
41359                             
41360                             Roo.log("our code?");       
41361                             //Roo.log(success);
41362                             //Roo.log(response)
41363                             delete this.activeRequest;
41364                             if(!success){
41365                                 this.fireEvent("loadexception", this, o, response);
41366                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41367                                 return;
41368                             }
41369                             var result;
41370                             try {
41371                                 result = o.reader.read(response);
41372                             }catch(e){
41373                                 Roo.log("load exception?");
41374                                 this.fireEvent("loadexception", this, o, response, e);
41375                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41376                                 return;
41377                             }
41378                             Roo.log("ready...");        
41379                             // loop through result.records;
41380                             // and set this.tdate[date] = [] << array of records..
41381                             _this.tdata  = {};
41382                             Roo.each(result.records, function(r){
41383                                 //Roo.log(r.data);
41384                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41385                                     _this.tdata[r.data.when_dt.format('j')] = [];
41386                                 }
41387                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41388                             });
41389                             
41390                             //Roo.log(_this.tdata);
41391                             
41392                             result.records = [];
41393                             result.totalRecords = 6;
41394                     
41395                             // let's generate some duumy records for the rows.
41396                             //var st = _this.dateField.getValue();
41397                             
41398                             // work out monday..
41399                             //st = st.add(Date.DAY, -1 * st.format('w'));
41400                             
41401                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41402                             
41403                             var firstOfMonth = date.getFirstDayOfMonth();
41404                             var days = date.getDaysInMonth();
41405                             var d = 1;
41406                             var firstAdded = false;
41407                             for (var i = 0; i < result.totalRecords ; i++) {
41408                                 //var d= st.add(Date.DAY, i);
41409                                 var row = {};
41410                                 var added = 0;
41411                                 for(var w = 0 ; w < 7 ; w++){
41412                                     if(!firstAdded && firstOfMonth != w){
41413                                         continue;
41414                                     }
41415                                     if(d > days){
41416                                         continue;
41417                                     }
41418                                     firstAdded = true;
41419                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41420                                     row['weekday'+w] = String.format(
41421                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41422                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41423                                                     d,
41424                                                     date.format('Y-m-')+dd
41425                                                 );
41426                                     added++;
41427                                     if(typeof(_this.tdata[d]) != 'undefined'){
41428                                         Roo.each(_this.tdata[d], function(r){
41429                                             var is_sub = '';
41430                                             var deactive = '';
41431                                             var id = r.id;
41432                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41433                                             if(r.parent_id*1>0){
41434                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41435                                                 id = r.parent_id;
41436                                             }
41437                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41438                                                 deactive = 'de-act-link';
41439                                             }
41440                                             
41441                                             row['weekday'+w] += String.format(
41442                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41443                                                     id, //0
41444                                                     r.product_id_name, //1
41445                                                     r.when_dt.format('h:ia'), //2
41446                                                     is_sub, //3
41447                                                     deactive, //4
41448                                                     desc // 5
41449                                             );
41450                                         });
41451                                     }
41452                                     d++;
41453                                 }
41454                                 
41455                                 // only do this if something added..
41456                                 if(added > 0){ 
41457                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41458                                 }
41459                                 
41460                                 
41461                                 // push it twice. (second one with an hour..
41462                                 
41463                             }
41464                             //Roo.log(result);
41465                             this.fireEvent("load", this, o, o.request.arg);
41466                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41467                         },
41468                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41469                     proxy : {
41470                         xtype: 'HttpProxy',
41471                         xns: Roo.data,
41472                         method : 'GET',
41473                         url : baseURL + '/Roo/Shop_course.php'
41474                     },
41475                     reader : {
41476                         xtype: 'JsonReader',
41477                         xns: Roo.data,
41478                         id : 'id',
41479                         fields : [
41480                             {
41481                                 'name': 'id',
41482                                 'type': 'int'
41483                             },
41484                             {
41485                                 'name': 'when_dt',
41486                                 'type': 'string'
41487                             },
41488                             {
41489                                 'name': 'end_dt',
41490                                 'type': 'string'
41491                             },
41492                             {
41493                                 'name': 'parent_id',
41494                                 'type': 'int'
41495                             },
41496                             {
41497                                 'name': 'product_id',
41498                                 'type': 'int'
41499                             },
41500                             {
41501                                 'name': 'productitem_id',
41502                                 'type': 'int'
41503                             },
41504                             {
41505                                 'name': 'guid',
41506                                 'type': 'int'
41507                             }
41508                         ]
41509                     }
41510                 },
41511                 toolbar : {
41512                     xtype: 'Toolbar',
41513                     xns: Roo,
41514                     items : [
41515                         {
41516                             xtype: 'Button',
41517                             xns: Roo.Toolbar,
41518                             listeners : {
41519                                 click : function (_self, e)
41520                                 {
41521                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41522                                     sd.setMonth(sd.getMonth()-1);
41523                                     _this.monthField.setValue(sd.format('Y-m-d'));
41524                                     _this.grid.ds.load({});
41525                                 }
41526                             },
41527                             text : "Back"
41528                         },
41529                         {
41530                             xtype: 'Separator',
41531                             xns: Roo.Toolbar
41532                         },
41533                         {
41534                             xtype: 'MonthField',
41535                             xns: Roo.form,
41536                             listeners : {
41537                                 render : function (_self)
41538                                 {
41539                                     _this.monthField = _self;
41540                                    // _this.monthField.set  today
41541                                 },
41542                                 select : function (combo, date)
41543                                 {
41544                                     _this.grid.ds.load({});
41545                                 }
41546                             },
41547                             value : (function() { return new Date(); })()
41548                         },
41549                         {
41550                             xtype: 'Separator',
41551                             xns: Roo.Toolbar
41552                         },
41553                         {
41554                             xtype: 'TextItem',
41555                             xns: Roo.Toolbar,
41556                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41557                         },
41558                         {
41559                             xtype: 'Fill',
41560                             xns: Roo.Toolbar
41561                         },
41562                         {
41563                             xtype: 'Button',
41564                             xns: Roo.Toolbar,
41565                             listeners : {
41566                                 click : function (_self, e)
41567                                 {
41568                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41569                                     sd.setMonth(sd.getMonth()+1);
41570                                     _this.monthField.setValue(sd.format('Y-m-d'));
41571                                     _this.grid.ds.load({});
41572                                 }
41573                             },
41574                             text : "Next"
41575                         }
41576                     ]
41577                 },
41578                  
41579             }
41580         };
41581         
41582         *//*
41583  * Based on:
41584  * Ext JS Library 1.1.1
41585  * Copyright(c) 2006-2007, Ext JS, LLC.
41586  *
41587  * Originally Released Under LGPL - original licence link has changed is not relivant.
41588  *
41589  * Fork - LGPL
41590  * <script type="text/javascript">
41591  */
41592  
41593 /**
41594  * @class Roo.LoadMask
41595  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41596  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41597  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41598  * element's UpdateManager load indicator and will be destroyed after the initial load.
41599  * @constructor
41600  * Create a new LoadMask
41601  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41602  * @param {Object} config The config object
41603  */
41604 Roo.LoadMask = function(el, config){
41605     this.el = Roo.get(el);
41606     Roo.apply(this, config);
41607     if(this.store){
41608         this.store.on('beforeload', this.onBeforeLoad, this);
41609         this.store.on('load', this.onLoad, this);
41610         this.store.on('loadexception', this.onLoadException, this);
41611         this.removeMask = false;
41612     }else{
41613         var um = this.el.getUpdateManager();
41614         um.showLoadIndicator = false; // disable the default indicator
41615         um.on('beforeupdate', this.onBeforeLoad, this);
41616         um.on('update', this.onLoad, this);
41617         um.on('failure', this.onLoad, this);
41618         this.removeMask = true;
41619     }
41620 };
41621
41622 Roo.LoadMask.prototype = {
41623     /**
41624      * @cfg {Boolean} removeMask
41625      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41626      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41627      */
41628     /**
41629      * @cfg {String} msg
41630      * The text to display in a centered loading message box (defaults to 'Loading...')
41631      */
41632     msg : 'Loading...',
41633     /**
41634      * @cfg {String} msgCls
41635      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41636      */
41637     msgCls : 'x-mask-loading',
41638
41639     /**
41640      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41641      * @type Boolean
41642      */
41643     disabled: false,
41644
41645     /**
41646      * Disables the mask to prevent it from being displayed
41647      */
41648     disable : function(){
41649        this.disabled = true;
41650     },
41651
41652     /**
41653      * Enables the mask so that it can be displayed
41654      */
41655     enable : function(){
41656         this.disabled = false;
41657     },
41658     
41659     onLoadException : function()
41660     {
41661         Roo.log(arguments);
41662         
41663         if (typeof(arguments[3]) != 'undefined') {
41664             Roo.MessageBox.alert("Error loading",arguments[3]);
41665         } 
41666         /*
41667         try {
41668             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41669                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41670             }   
41671         } catch(e) {
41672             
41673         }
41674         */
41675     
41676         
41677         
41678         this.el.unmask(this.removeMask);
41679     },
41680     // private
41681     onLoad : function()
41682     {
41683         this.el.unmask(this.removeMask);
41684     },
41685
41686     // private
41687     onBeforeLoad : function(){
41688         if(!this.disabled){
41689             this.el.mask(this.msg, this.msgCls);
41690         }
41691     },
41692
41693     // private
41694     destroy : function(){
41695         if(this.store){
41696             this.store.un('beforeload', this.onBeforeLoad, this);
41697             this.store.un('load', this.onLoad, this);
41698             this.store.un('loadexception', this.onLoadException, this);
41699         }else{
41700             var um = this.el.getUpdateManager();
41701             um.un('beforeupdate', this.onBeforeLoad, this);
41702             um.un('update', this.onLoad, this);
41703             um.un('failure', this.onLoad, this);
41704         }
41705     }
41706 };/*
41707  * Based on:
41708  * Ext JS Library 1.1.1
41709  * Copyright(c) 2006-2007, Ext JS, LLC.
41710  *
41711  * Originally Released Under LGPL - original licence link has changed is not relivant.
41712  *
41713  * Fork - LGPL
41714  * <script type="text/javascript">
41715  */
41716
41717
41718 /**
41719  * @class Roo.XTemplate
41720  * @extends Roo.Template
41721  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41722 <pre><code>
41723 var t = new Roo.XTemplate(
41724         '&lt;select name="{name}"&gt;',
41725                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41726         '&lt;/select&gt;'
41727 );
41728  
41729 // then append, applying the master template values
41730  </code></pre>
41731  *
41732  * Supported features:
41733  *
41734  *  Tags:
41735
41736 <pre><code>
41737       {a_variable} - output encoded.
41738       {a_variable.format:("Y-m-d")} - call a method on the variable
41739       {a_variable:raw} - unencoded output
41740       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41741       {a_variable:this.method_on_template(...)} - call a method on the template object.
41742  
41743 </code></pre>
41744  *  The tpl tag:
41745 <pre><code>
41746         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41747         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41748         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41749         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41750   
41751         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41752         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41753 </code></pre>
41754  *      
41755  */
41756 Roo.XTemplate = function()
41757 {
41758     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41759     if (this.html) {
41760         this.compile();
41761     }
41762 };
41763
41764
41765 Roo.extend(Roo.XTemplate, Roo.Template, {
41766
41767     /**
41768      * The various sub templates
41769      */
41770     tpls : false,
41771     /**
41772      *
41773      * basic tag replacing syntax
41774      * WORD:WORD()
41775      *
41776      * // you can fake an object call by doing this
41777      *  x.t:(test,tesT) 
41778      * 
41779      */
41780     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41781
41782     /**
41783      * compile the template
41784      *
41785      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41786      *
41787      */
41788     compile: function()
41789     {
41790         var s = this.html;
41791      
41792         s = ['<tpl>', s, '</tpl>'].join('');
41793     
41794         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41795             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41796             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41797             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41798             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41799             m,
41800             id     = 0,
41801             tpls   = [];
41802     
41803         while(true == !!(m = s.match(re))){
41804             var forMatch   = m[0].match(nameRe),
41805                 ifMatch   = m[0].match(ifRe),
41806                 execMatch   = m[0].match(execRe),
41807                 namedMatch   = m[0].match(namedRe),
41808                 
41809                 exp  = null, 
41810                 fn   = null,
41811                 exec = null,
41812                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41813                 
41814             if (ifMatch) {
41815                 // if - puts fn into test..
41816                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41817                 if(exp){
41818                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41819                 }
41820             }
41821             
41822             if (execMatch) {
41823                 // exec - calls a function... returns empty if true is  returned.
41824                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41825                 if(exp){
41826                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41827                 }
41828             }
41829             
41830             
41831             if (name) {
41832                 // for = 
41833                 switch(name){
41834                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41835                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41836                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41837                 }
41838             }
41839             var uid = namedMatch ? namedMatch[1] : id;
41840             
41841             
41842             tpls.push({
41843                 id:     namedMatch ? namedMatch[1] : id,
41844                 target: name,
41845                 exec:   exec,
41846                 test:   fn,
41847                 body:   m[1] || ''
41848             });
41849             if (namedMatch) {
41850                 s = s.replace(m[0], '');
41851             } else { 
41852                 s = s.replace(m[0], '{xtpl'+ id + '}');
41853             }
41854             ++id;
41855         }
41856         this.tpls = [];
41857         for(var i = tpls.length-1; i >= 0; --i){
41858             this.compileTpl(tpls[i]);
41859             this.tpls[tpls[i].id] = tpls[i];
41860         }
41861         this.master = tpls[tpls.length-1];
41862         return this;
41863     },
41864     /**
41865      * same as applyTemplate, except it's done to one of the subTemplates
41866      * when using named templates, you can do:
41867      *
41868      * var str = pl.applySubTemplate('your-name', values);
41869      *
41870      * 
41871      * @param {Number} id of the template
41872      * @param {Object} values to apply to template
41873      * @param {Object} parent (normaly the instance of this object)
41874      */
41875     applySubTemplate : function(id, values, parent)
41876     {
41877         
41878         
41879         var t = this.tpls[id];
41880         
41881         
41882         try { 
41883             if(t.test && !t.test.call(this, values, parent)){
41884                 return '';
41885             }
41886         } catch(e) {
41887             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41888             Roo.log(e.toString());
41889             Roo.log(t.test);
41890             return ''
41891         }
41892         try { 
41893             
41894             if(t.exec && t.exec.call(this, values, parent)){
41895                 return '';
41896             }
41897         } catch(e) {
41898             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41899             Roo.log(e.toString());
41900             Roo.log(t.exec);
41901             return ''
41902         }
41903         try {
41904             var vs = t.target ? t.target.call(this, values, parent) : values;
41905             parent = t.target ? values : parent;
41906             if(t.target && vs instanceof Array){
41907                 var buf = [];
41908                 for(var i = 0, len = vs.length; i < len; i++){
41909                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41910                 }
41911                 return buf.join('');
41912             }
41913             return t.compiled.call(this, vs, parent);
41914         } catch (e) {
41915             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41916             Roo.log(e.toString());
41917             Roo.log(t.compiled);
41918             return '';
41919         }
41920     },
41921
41922     compileTpl : function(tpl)
41923     {
41924         var fm = Roo.util.Format;
41925         var useF = this.disableFormats !== true;
41926         var sep = Roo.isGecko ? "+" : ",";
41927         var undef = function(str) {
41928             Roo.log("Property not found :"  + str);
41929             return '';
41930         };
41931         
41932         var fn = function(m, name, format, args)
41933         {
41934             //Roo.log(arguments);
41935             args = args ? args.replace(/\\'/g,"'") : args;
41936             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41937             if (typeof(format) == 'undefined') {
41938                 format= 'htmlEncode';
41939             }
41940             if (format == 'raw' ) {
41941                 format = false;
41942             }
41943             
41944             if(name.substr(0, 4) == 'xtpl'){
41945                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41946             }
41947             
41948             // build an array of options to determine if value is undefined..
41949             
41950             // basically get 'xxxx.yyyy' then do
41951             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41952             //    (function () { Roo.log("Property not found"); return ''; })() :
41953             //    ......
41954             
41955             var udef_ar = [];
41956             var lookfor = '';
41957             Roo.each(name.split('.'), function(st) {
41958                 lookfor += (lookfor.length ? '.': '') + st;
41959                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41960             });
41961             
41962             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41963             
41964             
41965             if(format && useF){
41966                 
41967                 args = args ? ',' + args : "";
41968                  
41969                 if(format.substr(0, 5) != "this."){
41970                     format = "fm." + format + '(';
41971                 }else{
41972                     format = 'this.call("'+ format.substr(5) + '", ';
41973                     args = ", values";
41974                 }
41975                 
41976                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41977             }
41978              
41979             if (args.length) {
41980                 // called with xxyx.yuu:(test,test)
41981                 // change to ()
41982                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41983             }
41984             // raw.. - :raw modifier..
41985             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41986             
41987         };
41988         var body;
41989         // branched to use + in gecko and [].join() in others
41990         if(Roo.isGecko){
41991             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41992                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41993                     "';};};";
41994         }else{
41995             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
41996             body.push(tpl.body.replace(/(\r\n|\n)/g,
41997                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41998             body.push("'].join('');};};");
41999             body = body.join('');
42000         }
42001         
42002         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42003        
42004         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42005         eval(body);
42006         
42007         return this;
42008     },
42009
42010     applyTemplate : function(values){
42011         return this.master.compiled.call(this, values, {});
42012         //var s = this.subs;
42013     },
42014
42015     apply : function(){
42016         return this.applyTemplate.apply(this, arguments);
42017     }
42018
42019  });
42020
42021 Roo.XTemplate.from = function(el){
42022     el = Roo.getDom(el);
42023     return new Roo.XTemplate(el.value || el.innerHTML);
42024 };